我们知道面对对象(OOP)的三大特点是:继承,封装,多态(重载,重写)
js并不是严格的面对对象的语言,因为js的面对对象都是基于原型链实现的。
原型链是由__proto__串联起来的链状结构。每一个函数都有一个prototype属性,也有__proto__,每一个对象都只有一个 __proto__属性;一般我们把属性方法构造函数中,把方法挂在prototype下面,对象的 __proto__指向构造函数的prototype,原型链的终点为Object.prototype,再往上Object.prototype.__proto__=null
1.实例没有具体的构造函数
let obj={}
console.log(obj.__proto__== Object.prototype) //true
console.log(Object.prototype.__proto__) //null
2.实例的构造函数为自定义的构造函数
function Persion(){}//构造函数
let p=new Persion()//实例
console.log(p.__proto__==Persion.prototype) //true
console.log(Persion.prototype.__proto__==Object.prototype) //true
console.log(Object.prototype.__proto__) //null
3.实例的构造函数为内置构造函数
let p = new Array()
console.log(p.__proto__ == Array.prototype) //true
console.log(Array.__proto__ ==Function.prototype)//true
console.log(Function.__proto__ == Function.prototype)//true ,Function 是构造函数,也是对象
console.log(Function.prototype.__proto__==Object.prototype)//true
console.log(Object.prototype.__proto__)//null
Function构造函数的构造函数就是他本身,Function作为实例化对象访问Function构造函数是可行的,所以Function.__proto__可以访问Function.prototype;任何函数都继承Function的所有方法以及属性,而Function是内置的构造函数,也是对象,都是继承Object的所有属性和方法,Function.prototype.__proto__=Object.prototype
首先在这里写一个Persion父类,该类有一个属性为name,有一个实例方法introduce,有一个原型方法hobby
function Parent(name){
this.name=name
this.introduce=function(){
console.log("my name is"+this.name)
}
}
Parent.prototype.hobby=function(hobby){
console.log(this.name+" like "+hobby)
}
子类的原型指向父类的实例(子类的.prototype=new 父类())
function Child() {} //子类
// Child.prototype.sleep=function(){
// console.log(this.name+"is sleeping")
// }1
Child.prototype = new Parent("yzh")
//为了不破坏原型链,将Male的constructor指向本身
Child.prototype.constructor=Child
Child.prototype.sleep=function(){
console.log(this.name+" is sleeping")
}//2
Parent.prototype.eat=function(){
console.log(this.name+" is eating")
}
var child=new Child()
console.log(Child)
child.introduce()
child.hobby("唱歌")
child.sleep()
child.eat()
console.log(child instanceof Parent) //true
console.log(child instanceof Child) //true
注:如上如果Male的原型方法sleep放在1位置时,会报错child.sleep is not a function,放在2位置,输出 yzh is sleeping
此种继承方法的特点:
缺点:
把 父对象的所有属性和方法,拷贝进子对象
function Child(name){
Parent.call(this,name)
}
var child=new Child("yzh")
console.log(child.name) //yzh
child.introduce()
child.hobby("sing")
console.log(child instanceof Parent) //false
console.log(child instanceof Child) //true
优点:
缺点:
function Child(name) {
Parent.call(this,name) //构造继承 ,第二次调用父类
}
//原型链继承
Child.prototype=new Parent()
Child.prototype.constructor=Child
var child=new Child("yzh") //子类的实例向父类传递参数,第一次调用父类
console.log(child.name)
child.introduce()
child.hobby("sing")
console.log(child instanceof Parent) //true
console.log(child instanceof Child) //true
优点:结合了原型链继承和构造继承的优点
缺点:这种方式调用了两次父类的构造函数,生成了两份实例,相同的属性既存在于实例中也存在于原型中
function Child(name){
var parent = new Parent(name);
for(var key in parent){
Child.prototype[key] = parent[key];
}
}
var child=new Child("yzh")
console.log(child.name) //yzh
child.introduce()
child.hobby("sing")
console.log(child instanceof Parent) //false
console.log(child instanceof Child) //true
优点:
缺点:
寄生组合式继承:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法.
思路:不必为了指定子类的原型而调用父类的构造函数,我们所需要的无非就是父类原型的一个副本而已.本质上,就是使用寄生式继承来继承父类的原型,然后在将结果指定给子类的原型
其实就是在不需要实例化父类构造函数的情况下,也能得到父类的属性和方法,就是直接实例化一个临时的父类的副本,实现原型链继承
function Child(name) {
Parent.call(this,name) //构造继承
}
(function(){
//创建一个临时的类
var Temp=function(){}
Temp.prototype=Parent.prototype
//子类的原型指向父类的实例
Child.prototype=new Temp()
})()
var p=new Child("yzh")
console.log(p.name)//yzh
p.hobby("sing")//yzh like sing
p.introduce()//my name is yzh
优点:结合组合继承的所有优点,此方法比较完美
class Parent{
constructor(name){
this.name=name
}
introduce(){
console.log("my name is " + this.name)
}
hobby(hobby){
console.log(this.name + " like " + hobby)
}
}
class Child extends Parent{
constructor(name,age){
super(name) //构造继承,可以继承Parent构造函数上的属性
this.age=age
}
}
var p = new Child("yzh")
p.introduce() //my name is yzh
p.hobby("apple")//yzh like apple
console.log(p instanceof Parent) // true
console.log(p instanceof Child) //true
我们用es6的写法让对象的写法更加的清晰,更像面对对象的编程;在这个方法中使用extends 和 super 两个关键字,在子类的constructor方法中调用super方法,用来继承父类的this对象
<div class="box" id="box">box1不限定范围div>
<div class="box" id="box2">box2限定范围div>
//没有限定范围的拖拽
class Drag{
constructor(ele){
this.box=document.querySelector(ele)
this.addHandler()
}
addHandler(){
this.box.onmousedown=(e)=>{
let _x=e.offsetX //鼠标按下距离元素上边界的距离
let _y=e.offsetY
e.preventDefault()
//滑动与抬起事件绑定在document上
document.onmousemove=(e)=>{
let _left=e.clientX-_x //e.clientX 鼠标相对浏览器顶部的距离
let _top=e.clientY-_y
// console.log(_left,_top) //距离文档的距离
this.move(_left,_top)
}
document.onmouseup=()=>{
document.onmousemove=null
}
}
}
move(_left,_top){
this.box.style.left=_left+'px'
this.box.style.top=_top+'px'
}
}
//限定范围 继承Drag
class limitDrag extends Drag{
constructor(ele) {
super(ele)
}
//重写move函数
move(_left,_top){
if(_left<0)_left=0
if(_top<0)_top=0
if(_left>document.body.offsetWidth-this.box.offsetWidth) _left=document.body.offsetWidth-this.box.offsetWidth
if(_top>document.body.offsetHeight-this.box.offsetHeight) _top=document.body.offsetHeight-this.box.offsetHeight
this.box.style.left=_left+'px'
this.box.style.top=_top+'px'
}
}
new Drag('#box')
new limitDrag('#box2')