JS 中继承比较复杂,坑比较多,最近有点时间整理下,记录下来。
JS 的继承实现方式大概分类如下的两大类,每一种实现都有自己的有点和缺点,根据场景选择吧
通过修改原型链来来实现继承
通过复制父类来来实现继承
为了理解继承的原型链的变化,我画了原型链图。下图是没有继承的时候,父类和子类的原型链图
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(){}
修改原型链来来实现继承
修改原型链也有好几种,现在分开来说:
第一种
实现原理:修改原型链(子类的原型指向父类的原型)实现继承
优点:简单
缺点:子类修改影响父类
原型链图注意里面的红线
不调用构造方法实现
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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。