原型链继承
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