原型链继承
1  | function Parent() {  | 
原型链继承
优点:
- 方法复用,由于方法定义在父类的原型上,复用了父类构造函数的方法。比如greeting方法。
 
缺点:
- 创建子类实例的时候,不能传参数。
 - 子类实例共享了父类构造函数的引用属性,比如names属性。child1.names修改会影响child2.names的值
 - 但是实例子类基本类型属性不会影响其他子类基本类型属性。child1.age
 
借用构造函数(经典继承)
1  | function Parent () {  | 
1  | function Parent (name) { //创建子类实例,可以向父类构造函数传参数。  | 
借用构造函数
优点:
- 避免了引用类型的属性被所有实例共享,比如names属性
 - 创建子类实例,可以向父类构造函数传参数。
 
缺点:
- 父类的方法不能复用
 - 方法都在构造函数中定义,每次创建实例都会创建一遍方法。
 
组合继承
原型链继承和构造函数组合拳1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
    console.log(this.name)
}
//构造函数
function Child (name, age) {
    Parent.call(this, name);    一次函数调用
    
    this.age = age;
}
Child.prototype = new Parent();   //原型链继承   二次函数调用
Child.prototype.constructor = Child;  //修复Child.prototype.constructor
var child1 = new Child('foo', '18');
child1.colors.push('black');
console.log(child1.name); // foo
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]
var child2 = new Child('bar', '20');
console.log(child2.name); // bar
console.log(child2.age); // 20
console.log(child2.colors); // ["red", "blue", "green"]
组合继承
优点:
- 保留构造函数的优点:创建子类实例,可以向父类构造函数传参数。
 - 保留原型链的优点:父类的实例方法定义在父类的原型对象上,可以实现方法复用。
 - 不共享父类的引用属性。比如colors属性
 
缺点:
- 由于调用了2次父类的构造方法,会存在一份多余的父类实例属性
注意:‘组合继承’这种方式,要记得修复Child.prototype.constructor指向
 
寄生组合继承——完美方式
1  | function Parent(name) {  | 
代码优化:1
2
3
4
5
6
7
8
9
10
11function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}
function prototype(child, parent) {
    var prototype = object(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype;
// 当我们使用的时候:
prototype(Child, Parent);
寄生组合继承
优点:完美
缺点:理论上没有
ES6继承
ECMAScript6 引入了一套新的关键字用来实现 class。使用基于类语言(java,php等)的开发人员会对这些结构感到熟悉,但它们是不一样的。 JavaScript 仍然是基于原型的。这些新的关键字包括 class , constructor , static , extends , 和 super . 例子如下:
1  | class Animal {  | 
class和自定义类型的区别
- class的声明不会提升,与let类似
 - class的声明自动运行于严格模式之下
 - class声明的方法不可枚举(显著区别)
 - class的内部方法没有[[construct]]属性,无法new
 - 调用class的构造函数必须new
 - class内部方法不能同名
 
ES6继承小结:
- ES6中class简化了ES5中的继承,但是未改变现有的继承模型。可以理解为是ES5基于原型链的语法糖
 - 通过class声明一个类,constructor()作为构造函数,属性在constructor()中初始化
 - class内可以定义getter/setter访问器属性
 - 可以在class内定义非静态方法,静态方法绑定在构造器上
 - 类的所有方法都是不可枚举的,也符合内部方法
 - 实例化一个class必须要new关键字
 - extends实现继承,子类中调用super()访问父类构造函数
 - 因为class的实现是基于ES5类模型那一套,本质上和ES5中是一样的,如果过多使用extends可能还会降低性能
 
ES6 继承推荐看阮一峰的ES6