①原型链继承
将父类的实例作为子类的原型
【因为实例对象是沿着原型链进行继承的(和构造函数无关)】
//爸爸
function Father () {
//
}
//儿子
function Son () {
//
}
//原型链继承核心
Son.prototype = new Father()
//如果不手动指回来的话 Son.prototype.constructor = Father了
//因为Son.prototype是Father的实例赋值过来的
//那么Father 的实例的constructor是Father
//为了避免这种情况,就手动指回来
Son.prototype.constructor = Son;
优点:
父类可以复用
缺点:
父类原型所有的属性方法被所有实例共享,如果改变所有实例都会被影响
子类实例无法向父类构造函数传参
②构造函数继承
在儿子的构造函数中利用call、apply调用父亲
new做了什么事
new一个实例的步骤:
先创建一个空的对象
让这个空对象的 ‘proto’ -> 父构造函数的ptototype
让这个创建的空对象的this指向new的构造函数,然后初始化构造函数里面this的属性和方法
new一个Son构造函数,然后传的参数是’dpn’,在构造函数里面先执行了this.name = name,这个时候在这个dpn对象里面就有一个name = ‘dpn’,这个时候执行了Father函数并且把this指向了Son【子构造函数】,所以在子构造函数里面有了父构造函数里面的属性和方法,这些属性和方法的this指向后来调用它的对象
后面执行dpn.eat(),这个时候,子构造函数里面的this指向了dpn这个对象,就会执行eat()这个方法,打印this.name就是dpn在最开始的时候初始化的哪个name,打印结果就为//dpn can eating i am father
如果使用传参数的方式打印this.name,那么可以在call里面进行传 name 参数,如果不传,在eat()方法里的this.name就会是父构造函数里面的后来重新定义this.name,虽然值是一样的,但是覆盖了原来,但是又没有传过来,所以就会是 undefined
正确的传参数的方式来打印,那么就要在传参和实参两面都要兼顾
//爸爸
function Father (name) {
//this.name = name
this.eat = function () {
console.log(this.name + ' can eating i am father');
}
}
//儿子
function Son (name) {
this.name = name
//构造函数核心
Father.call(this, 可以传参数 //name)
}
var dpn = new Son('dpn')
dpn.eat() // dpn can eating i am father
优点:
父类的引用属性不会被共享
可以在子类构造函数中向父构造函数传参数
缺点:
子类不能访问父类原型上定义的方法
③组合继承
综合前两种:既可以继承父构造函数的属性和方法,也可以继承父构造函数原型的属性和方法
在子构造函数里面调用Father.call,在外面给子构造函数的原型赋值为父构造函数的实例,然后改子类的构造函数为自己
缺点:
执行了两次父构造函数,所以产生了两份实例
//爸爸
function Father (name) {
//this.name = name
this.eat = function () {
console.log(this.name + ' can eating i am father');
}
}
//儿子
function Son (name) {
this.name = name
//构造函数核心
Father.call(this, 可以传参数 //name) //--------2
}
Son.prototype = new Father() //---------1
//将构造函数指回自己
Son.prototype.constructor = Son;
var dpn = new Son('dpn')
dpn.eat() // dpn can eating i am father
④原型式继承
对原型链继承的一种封装
让一个对象和另外一个对象建立继承关系,在ES5中规范了这个方法 Object.create(obj)
中间有一个干净的类(寄生虫)
function extent (o) {
//干净的类
function A () {}
A.prototype = o
return A
}
//使用
Son.prototype = extent(Father)
⑤寄生式继承
原型式继承的加强版 寄生继承是依托于一个对象而生的一种继承方式
function fun (father) {
var o = Object.create(father)
//为这个继承父构造函数的对象添加一些增强方法
o.prototype.方法 = function () {
//方法体
}
return o
}
var o = new fun(Father);//
⑥寄生组合继承
组合继承和寄生式的升级版本,
实质:不必为了指定子类的原型而调用父类构造函数,我们只需父类的原型副本即可【也就是创造出一个寄生虫,让他从继承父类的原型】
使用寄生式继承来继承父类原型,然后将结果(实例)指定给子类原型。
代替原型链继承的代码
//Son.prototype = new Father()
//将构造函数指回自己
//Son.prototype.constructor = Son;
function fun (father, son) {
//搞一个新的对象,让他的原型等于爸爸的原型
var o = Object.create(father.prototype)
//让儿子的原型等于这个对象的原型,也就是等于爸爸的原型
son.prototype = o
//让这个对象构造函数指向儿子
o.constructor = son
}
//执行
fun(Father, Son)
//修复构造函数指向问题
Son.prototype.constructor = Son;
优点:
-
子类继承了父类的属性和方法,同时,属性没有被创建在原型链上,因此多个子类不会共享同一个属性。
-
子类可以传递动态参数给父类!
-
父类的构造函数只执行了一次!