一.原型链继承
原型链继承主要是通过修改原型中的prototype
的指向,从而继承上级构造函数的公有属性
function Fn1 (name, age) {
this.name = name;
this.age = age;
}
// Fn1扩展原型
Fn1.prototype.say = function () {
alert('hello');
}
var father = new Fn1('大李', 30);
function Fn2 (name, age) {
this.name = name;
this.age = age;
}
两构造函数的结构图如下图
现在需要Fn2
创建的实例也能够使用say函数。但是,实例中的__proto__
属性是不允许进行修改的,所以要想达到目的只有更改Fn2
中prototype
的指向。目前有1,2两条可修改的线路,但是对于线路2,即Fn1.prototype = Fn2.prototype
来说,存在缺陷:如果以后对Fn2
的prototype
的任何修改都会同时修改Fn1的prototype
,所以线路2不可行,选择线路1进行修改
通过将上级函数的实例赋值给下级函数的原型,用来代替下级函数的原型,从而构造出原型链
Fn2.prototype = new Fn1();
以上就完成了原型链的搭建,但是为了原始结构的完整性,在Fn1
的原型中修复constructor
指向Fn2
,即Fn2.prototype.constructor = Fn2
function Fn1 (name, age) {
this.name = name;
this.age = age;
}
// Fn1扩展原型
Fn1.prototype.say = function () {
alert('hello');
}
var father = new Fn1('大李', 30);
function Fn2 (name, age) {
this.name = name;
his.age = age;
}
// 原型链构建
Fn2.prototype = new Fn1();
// constructor修复
Fn2.prototype.constructor = Fn2;
// Fn2扩展原型
Fn2.prototype.eat = function () {
alert('fish');
}
var son = new Fn2('小李', 18);
console.log(son);
二.组合方式继承
Fn1
和Fn2
创建实例时都会创建相同的属性,如何让私有属性也能继承呢?改变Fn1
中this
指针到Fn2
就能解决问题
call
或apply
改进原型链
call
和apply
用法相似,相同点是第一个参数都是改变this
指针到指定对象。不同点是call
其余参数为对象的属性,如:Fn1.call(Fn2, name, age);apply
只有两个参数,第二个参数为属性组成的数组,如:Fn1.call(Fn2, [name, age])
// 只需将原来的Fn2改为如下即可
function Fn2 (name, age) {
Fn1.call(this, name, age);// this指的是Fn2
// Fn1.apply(this, [name, age]);
}
所有组合方式继承的本质就是结合call
或apply
函数对原型链进行进一步完善
组合方式继承不仅仅只能继承上级,还能继承多个对象的私有属性
function Fn2 (name, age, height, weight) {
Fn1.call(this, name, age);
Fn3.call(this, height);
Fn4.call(this, weight);
}
三.寄生方式继承
从son
的打印结果可以看出,虽然从上级全部继承,但是上级的原有私有属性也继承到了prototype
里且为undefined
,以寄生方式继承的目的就是为了出去上级继承的空属性
如何才能把从上级继承的为undefined
的属性去掉了?如果我们从上级只继承公有属性该多好,如果能继承有公有属性但没有私有属性的对象呢?
寄生方式继承的主要思想就是用一个临时的空的过渡函数去获取上级函数的prototype
,然后让下级函数继承这个空过渡函数
// 创建过渡函数
function Temp(){}
// 替换过渡函数原型
Temp.prototype = Fn1.prototype;
// 原型方式继承
Fn2.prototype = new Temp();
通过过渡替换的方法先让Temp
的原型变为Fn1的原型,因为Temp
没有私有属性,所有继承的Fn2
不会携带多余属性