代码复用一直是咱们程序员所追求的远大目标,毕竟能够少写点代码,何乐而不为呢?当说到代码复用的时候,最早想到的是继承,JavaScript对象上有本身的属性,也有一些属性是从原型对象继承来的,下面咱们来看看实现继承的几种方式: 程序员
1.传统模式——原型链闭包
用原型链的继承时,不只继承了自身的属性,并且继承了原型上的属性和方法。函数
看代码:this
//原型链继承 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();
咱们先看下这种继承模式的工做原理: spa
当咱们用new Son()来建立一个对象时,会建立一个图(3),里面保存了age的属性,若是咱们访问lastName属性时,尽管图(3)并无lastName这个属性,可是经过Son()的prototype原型上的隐式属性__proto__能够访问到图(1)中的lastName属性,这个__proto__就是原型链,这个属性只能系统内部去使用,开发者是用不了的。 prototype
咱们还要注意一点:若是咱们在控制台执行,son.name = "长江七号",这个操做并不会改变图(2)里面的name,它会直接在图(3)自身上建立一个属性,以下: 代理
原型链继承弊端:缺点在于同时继承了两个对象的属性,可是在大多的时候咱们并不须要这些属性,由于它们颇有可能指向一个特定的实例,而不是复用。 指针
2.继承——借用构造函数 code
借用构造函数的方式并不能彻底说是继承,就是借别人的方法来用下。对象
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();
『 好啦,以上呢就给你们的分享啦,若是您以为本篇内容有帮助到你,能够转载但记得要关注,要标明原文哦,谢谢支持~』
「欢迎各位大佬关注我,扫二维码便可」