Class + extend
Class 可以通过extends
关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}
想了解 Class + extend
可以前往ES6——Class 笔记和ES6——Class 的继承 笔记。
原型链
function Parent(){
this.age = 23;
}
Parent.prototype.getAge = function(){
return this.age;
};
function Child(){
this.name = "xd";
}
//继承了 Parent
Child.prototype = new Parent();
Child.prototype.getName = function(){
return this.name;
};
var child = new Child();
child.getAge() //23
问题:
- 包含引用类型值的原型属性会被所有实例共享。下列代码可以用来说明这个问题:
function Parent(){ this.friend = ['jh','zh']; } function Child(){} //继承了 Parent Child.prototype = new Parent(); var child1 = new Child(); child1.friend.push('zf'); var child2 = new Child(); child2.friend //["jh", "zh", "zf"]
- 在创建子类型的实例时,不能向超类型的构造函数中传递参数。
借用构造函数
借用构造函数可以解决包含引用类型值的原型属性会被所有实例共享这个问题。
function Parent(){
this.friend = ['jh','zh'];
}
function Child(){
//继承了 Parent
Parent.call(this);
}
var child1 = new Child();
child1.friend.push('zf');
var child2 = new Child();
child2.friend //["jh", "zh"]
相对于原型链而言,借用构造函数有一个很大的优势,即可以在子类型构造函数中向超类型构造函数传递参数。
function Parent(age){
this.age = age;
}
function Child(){
//继承了 Parent,同时还传递了参数
Parent.call(this,23);
//实例属性
this.name = 'xd';
}
var child = new Child();
child.age //23
child.name //'xd'
问题:方法都在构造函数中定义,因此函数复用就无从谈起了。
组合继承(常用)
组合继承的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
function Parent(age){
this.age = age;
this.friend = ['jh','zh'];
}
Parent.prototype.getAge = function(){
return this.age;
}
function Child(age, name){
//继承了属性
Parent.call(this, age);
this.name = name;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
Child.prototype.getName = function(){
return this.name;
}
var child1 = new Child(23, 'xd');
child1.friend.push('zf');
child1.age //23
child1.name //'xd'
child1.getName() //'xd'
child1.getage() //'23
var child2 = new Child(18, 'ah');
child2.friend //['jh','zh']
原型式继承
原型式继承的思想就是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
var person1 = {
name: "xd",
friends: ["jh", "zh"]
};
var person2 = Object.create(person1);
person2.name = "hc";
person2.friends.push("zf");
var person3 = Object.create(person1);
person3.name = "xs";
person3.friends.push("ah");
person1.friends //["jh", "zh", "zf", "ah"]
在没有必要兴师动众地创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式继承是完全可以胜任的。
问题:包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样。
寄生式继承
寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。
var createPerson = function(parent){
var child = Object.create(parent); //通过调用函数创建一个新对象
child.sayHi = function(){ //以某种方式来增强这个对象
console.log('Hi!');
}
return child; //返回这个对象
}
var person = {
name: 'xd',
friend: ['jh', 'zh']
};
var person2 = createPerson(person);
person2.sayHi(); //'Hi!'
问题:使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一点与构造函数模式类似。
寄生组合式继承
所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
function inheritPrototype(Child, Parent){
var parent = Object.create(Parent.prototype); //创建对象
parent.constructor = Child; //增强对象
Child.prototype = parent; //指定对象
}
function Parent(age){
this.age = age;
this.friend = ['jh','zh'];
}
Parent.prototype.getAge = function(){
return this.age;
}
function Child(age, name){
Parent.call(this, age);
this.name = name;
}
inheritPrototype(Child, Parent);
Child.prototype.getName = function(){
return this.name;
}