代码复用一直是我们程序员所追求的远大目标,毕竟可以少写点代码,何乐而不为呢?当说到代码复用的时候,最先想到的是继承,JavaScript 对象上有自己的属性,也有一些属性是从原型对象继承来的,下面我们来看看实现继承的几种方式:
1.传统模式——原型链
用原型链的继承时,不仅继承了自身的属性,而且继承了原型上的属性和方法。
看代码:
//原型链继承
Grand.prototype.lastName = '鲁班大师';
function Grand(){
//隐式链接 __proto__
}
var grand = new Grand();
Father.prototype = grand;
function Father(){
this.name = '小鲁班';
//隐式链接 __proto__
}
var father = new Father();
Son.prototype = father;
function Son(){
this.age = 18
//隐式链接 __proto__
}
var son = new Son();
复制代码
我们先看下这种继承模式的工作原理:
当我们用 new Son()来创建一个对象时,会创建一个图(3),里面保存了 age 的属性,如果我们访问 lastName 属性时,尽管图(3)并没有 lastName 这个属性,但是通过 Son()的 prototype 原型上的隐式属性__proto__可以访问到图(1)中的 lastName 属性,这个__proto__就是原型链,这个属性只能系统内部去使用,开发者是用不了的。
我们还要注意一点:如果我们在控制台执行,son.name = "长江七号",这个操作并不会改变图(2)里面的 name,它会直接在图(3)自身上创建一个属性,如下:
原型链继承弊端:缺点在于同时继承了两个对象的属性,但是在大多的时候我们并不需要这些属性,因为它们很有可能指向一个特定的实例,而不是复用。
2.继承——借用构造函数
借用构造函数的方式并不能完全说是继承,就是借别人的方法来用下。
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name,age,sex,grande){
Person.call(this,name,age,sex);
this.grande = grande;
}
var student = new Student();
复制代码
当我们要构造一个 Student 的构造函数,但是 Person 已经实现了我们部分功能,所以在 Student 中我们可以用 Person 中的方法就可以了。
借用构造函数弊端:
1.只能继承在构造函数中的方法,却不能继承那些添加到原型中的方法。
2.这种继承每次要多调用一个函数,只是在视觉上省代码,实际运行上还浪费效率了。
3.共享原型
共享原型的方式简单粗暴,让一个原型同时给两个函数,从而达到继承的目的,如图:
看代码:
Father.prototype.name = "长江七号"
function Father(){
var age = 18;
}
function Son(){
var sex = 'famle';
}
function inherit(Target,Origin){
Target.prototype = Origin.prototype;
}
inherit(Son,Father)
var son = new Son();
var father = new Father();
复制代码
尽管这两个对象共享了同一个原型,访问值的时候也很快,但是,这同时也是一个缺点,如果子对象修改了一个原型的属性,那么它会影响所有的祖对象。我们没有办法给子对象的原型单独给加属性。
属性修改过程:
以上三种继承方法多多少少都存在点缺点,接着引出第四种完美方法:
4.圣杯模式
圣杯模式其实跟共享原型的思路差不多,它是通过剪断父对象跟子对象的原型之间的直接关系,从而解决共享原型这一方法产生的问题,同时还可以继续共享原型上的属性,但改变子对象上原型的属性时,祖对象原型不受影响。如图所示:
看代码:
Father.prototype.name = "长江七号"
function Father(){
var age = 18;
}
function Son(){
var sex = 'famle';
}
function inherit(Target,Origin){
function F(){};//中间空白函数代理
F.prototype = Origin.prototype;
Target.prototype = new F();
}
inherit(Son,Father)
var son = new Son();
var father = new Father();
复制代码
⚠️注意:写圣杯模式的时候,我们一定要注意第 10 行和第 11 行,它们两个位置一定不能写反,必须在 new 之前改变原型指向,否则原型指向就指原来的自身原型上。**
接下来我们试试看好不好使:
ok!好使没问题,我们的目的达到了,几乎完美了,但是还差点,我们还需要重置构造函数指针,,如果不重置,那么所有子对象都会显示 Father()是它们的构造函数,造成指向紊乱了,所以我们可以归下位:
Father.prototype.name = "长江七号"
function Father(){
var age = 18;
}
function Son(){
var sex = 'famle';
}
function inherit(Target,Origin){
function F(){};
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;//重置构造函数
}
inherit(Son,Father)
var son = new Son();
var father = new Father();
复制代码
如果有一天我们想知道这个子对象真正继承自谁,在上面的基础上,我们还可以添加一个指向原始父对象的引用,加一个属性 uber,本来是可以用"super"的,但是由于 super 是保留字的关键字。改进后如下代码:
Father.prototype.name = "长江七号"
function Father(){
var age = 18;
}
function Son(){
var sex = 'famle';
}
function inherit(Target,Origin){
function F(){};
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;//重置构造函数
Target.prototype.uber = Origin.prototype;//访问超类,真正继承于谁
}
inherit(Son,Father)
var son = new Son();
var father = new Father();
复制代码
其实还有一种写法,在雅虎的 YUI 库里面有个 inherit 方法:利用闭包的私有化属性
Father.prototype.name = "长江七号"
function Father(){
var age = 18;
}
function Son(){
var sex = 'famle';
}
// function inherit(Target,Origin){
// function F(){};
// F.prototype = Origin.prototype;
// Target.prototype = new F();
// Target.prototype.constructor = Target;
// }
var inherit = (function(){
var F = function(){};
return function (Target,Origin){
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
}
}());
inherit(Son,Father)
var son = new Son();
var father = new Father();
复制代码
『 好啦,以上呢就给大家的分享啦,,谢谢支持~』
评论