JS 中继承比较复杂,坑比较多,最近有点时间整理下,记录下来。javascript
JS 的继承实现方式大概分类以下的两大类,每一种实现都有本身的有点和缺点,根据场景选择吧java
经过修改原型链来来实现继承git
经过复制父类来来实现继承es6
为了理解继承的原型链的变化,我画了原型链图。下图是没有继承的时候,父类和子类的原型链图github
function Parent(name, age) { this.name = name; this.age = age; } Parent.prototype.getName = function () { return this.name; }; Parent.prototype.getAge = function () { return this.age; }; Parent.prototype.love = { game: ['DoTa'] }; Parent.pay = function(){ return 1000; }; function Son(){}
修改原型链也有好几种,如今分开来讲:app
实现原理:修改原型链(子类的原型指向父类的原型)实现继承函数
优势:简单测试
缺点:子类修改影响父类this
原型链图注意里面的红线spa
不调用构造方法实现
function Son(){} Son.prototype = new Parent(); Son.prototype.constructor = Son;
调用构造方法实现
function Son(){ Parent.apply(this, arguments) } Son.prototype = Parent.prototype; Son.prototype.constructor = Son;
详细代码实现:https://github.com/xuanxiao2013/f2e-practice/blob/master/javascript-inherit/inherit1.js
实现原理:修改原型链(经过加入临时函数,阻止子类修改父类)实现继承
优势:子类即能继承父类,又基本不影响父类,达到真正意义上的继承
缺点:实例的对象和父类原型的对象相同的时候(父类的love),可能会出现子类修改父类对象原型中的全部属性被实例共享,共享很适合函数,对基本值的属性也能够(实例上添加同名属性),可是对引用类型的值的属性来讲,就会有问题
原型链图注意里面的红线
ES3 实现方式详细代码实现:
function create(proto){ var F = function(){}; F.prototype = proto; return new F(); } function Son(){ Parent.apply(this, arguments); } Son.prototype = create(Parent.prototype); Son.prototype.constructor = Son;
ES5 实现方式详细代码实现:
function Son(){ Parent.apply(this, arguments); } Son.prototype = Object.create(Parent.prototype) Son.prototype.constructor = Son;
ES6 实现方式 详细代码实现:
class Parent{ constructor(name, age){ this.name = name; this.age = age; } } Parent.prototype.getName = function () { return this.name; }; Parent.prototype.getAge = function () { return this.age; }; Parent.prototype.love = { game: ['DoTa'] }; class Son extends Parent { constructor(...args){ super(...args); } }
都说ES6 的Class 只是个语法糖,看来缘由在这了
测试:
var parent = new Parent('jack', 40); log('parentName:' + parent.getName()); // parentName:jack var son = new Son('tom', 20); Son.prototype.getName = function(){ return this.name + ' has good father'; }; log('sonName:' + son.getName()); // sonName:tom has good father log('parentName:' + parent.getName()); // parentName:jack log(Son.prototype.constructor === Son); // true log(son instanceof Son); // true log(son instanceof Parent); // true log(son instanceof Object); // true log('parent love:' + parent.love.game); // parent love:DoTa log('son love:' + son.love.game); // son love:DoTa log('------------------------'); // 注意这里 son.love.game = 'DoTa2'; log('parent love:' + parent.love.game); // parent love:DoTa2 log('son love:' + son.love.game); // son love:DoTa2 log('------------------------'); son.love = { game: 'DoTa3' }; // 注意这里 log('parent love:' + parent.love.game); // parent love:DoTa2 log('son love:' + son.love.game); // son love:DoTa3
实现原理:经过深度复制把父类的方法复制一份给子类来实现继承
优势:子类即能继承父类,又不影响父类,达到真正意义上的继承
缺点:复杂
详细代码实现:https://github.com/xuanxiao2013/f2e-practice/blob/master/javascript-inherit/inherit3.js