构造函数式继承
function Super(age){
this.nextYearAge = age;
}
Super.prototype.getNextYearAge = function(){
return `You will be {this.lastYearAge} next year`;
}
function Sub(age){
Super.call(this, age);
this.nowAge = age;
}
Sub.prototype.getNowAge = function(){
return `Now your age is ${this.nowAge}`;
}
const instance = new Sub(18);
console.log(instance.nowAge); // 18
console.log(instance.nextYearAge); // 19
console.log(instance.getNowAge()); // Now your age is 18
console.log(instance.getNextYearAge()); // TypeError: instance.getNextYearAge is not a function
可以看到,这种继承方式不可以继承父类构造函数的原型
通过原型链
function Super(){
this.friends = ['xiaoming', 'xiaohua', 'xiaoli'];
}
Super.prototype.getFriends = function(){
return this.friends.join(',');
}
function Sub(){};
Sub.prototype = new Super(); // Super的一个实例直接赋给Sub的原型
console.log(Sub.prototype.constructor); // [Function: Super]
const instance1 = new Sub();
const instance2 = new Sub();
console.log(instance1.friends);
console.log(instance2.friends);
instance1.friends.push('xiaoqiang');
console.log(instance1.getFriends());
console.log(instance2.getFriends());// instance1在实例引用类型上的修改也在instance2上反应出来
子类的实例也可以继承超类的属性和超类原型上的方法,但是子类原型的构造函数会变成超类。如果超类的属性值是引用类型,子类一个实例对该属性的修改也会在其它实例反应出来,这显然不是我们想要的结果
组合继承
function Super(...friends){
this.friends = friends;
};
Super.prototype.getFriends = function(){
return this.friends.join(',');
};
function Sub(friends, age){
Super.apply(this, friends);
this.age = age;
};
Sub.prototype = new Super();
Sub.prototype.getAge = function(){
return this.age;
};
const instance = new Sub(['xiaoming', 'xiaoli'], 18);
console.log(instance.getFriends());
console.log(instance.getAge());
// Sub的原型和实例上都添加了friends属性
console.log(instance.hasOwnProperty('friends')); // true
console.log(Sub.prototype.hasOwnProperty('friends')); // true
// Sub原型的constructor也不再指向自身
console.log(Sub.prototype.constructor); // [Function: Super]
组合模式是应用得最多的模式,但也不是没问题的,超类的构造函数被运行了两次,一次是给子类的原型赋值(也给子类的原型添加了属性,这大多不是我们想要的结果),另一次是子类生成实例时盗用了超类的构造函数来添加属性。我们还需要手动调用Object.defineProperty()来给子类原型添加constructor属性(否则Sub.constructor会顺着原型链查找,这里就会找到Super.prototype.constructor属性,当然值就是Super构造函数了)
寄生式组合继承
其背后的基本思路是:不必为了指定子类的原型而去调用父类的构造函数,我们需要的仅是超类原型的一个副本。也就是说,我们可以借助寄生式继承(一个空的构造函数)来继承超类型的原型,然后再把结果指定给子类型的原型。
function middle(obj){ //借用一个空的构造函数来引用超类的原型
const fn = new Function();
fn.prototype = obj;
return new fn();
};
function Super(...friends){
this.friends = friends;
};
Super.prototype.getFriends = function(){
return this.friends.join(',');
};
function Sub(friends, age){
Super.call(this, ...friends);
this.age = age;
};
Sub.prototype = middle(Super.prototype); // 这里只继承超类的原型,不需要执行超类的构造函数
Sub.prototype.getAge = function(){
return `Your age is ${this.age}`;
};
const instance = new Sub(['chenshun', 'xiaoqiang'], 18);
console.log(instance.getFriends()); // chenshun,xiaoqiang
console.log(instance.getAge()); // Your age is 18
console.log(Sub.prototype.hasOwnProperty('friends')); // false
console.log(Sub.prototype.hasOwnProperty('constructor')); // false
console.log(Sub.prototype.constructor); // [Function: Super]
这种模式仍然要手动为子类指定constructor属性。
类式继承
类式继承即囊括了寄生组合式继承的优点,又不用为子类重新设置constructor属性,简单易用。
class Super{
constructor(friends){
this.friends = friends;
}
getFrinends(){
return this.friends.join(',');
}
};
class Sub extends Super{
constructor(friends, age){
super(friends);
this.age = age;
}
getAge(){
return this.age;
}
}
const instance = new Sub(['chenshun', 'xiaoqiang'], 18);
console.log(instance.getFrinends());
console.log(instance.getAge());
// 不用手动指定constructor
console.log(Sub.prototype.hasOwnProperty('constructor')); // true
console.log(Sub.prototype.constructor); // [Function: Sub]