《JS高级程序设计》中说JS的继承有原型链、构造函数、组合式、原型式、寄生式、寄生组合式继承,加上ES6的class继承,一共有7种方式。前6种最关键的是搞清楚构造函数、原型对象、原型链的关系,其他模式都是这些的组合。
构造函数
构造函数也是函数,跟普通函数的没什么区别,但是可以用new()方法创建实例
e.g.
1 2 3 4 5
| function Animal(name, legCount) { this.name = name; this.legCount = legCount; } const cat = new Animal('Tom', 4);
|
原型对象
每个函数都有一个prototype属性(对象没有这个属性),指向一个原型对象,即Animal.prototype( 这玩意是个对象,名字就叫Animal.prototye )
Animal.prototype默认有个constructor属性,这个对象的constructor属性指向该对象对应的构造函数,即Animal()。
1 2 3 4 5 6
| console.log(Animal.prototype);
{ constructor: ƒ Animal(name ,leg), __proto__: Object }
|
注意:Animal()函数内部定义了name、age属性,但是Animal.prototype对象是没有这些属性的,只有默认的constructor属性,除非在原型上定义其他属性:
1 2 3 4 5 6 7 8
| Animal.prototype.speak = 'miao' console.log(Animal.prototype);
{ constructor: ƒ Animal(name ,leg), speak: 'miao', __proto__: Object }
|
这时创建一个Animal实例:
1
| const dog = new Animal('light', 4);
|
dog对象有一个[[prototype]]属性,指向该实例对象的原型对象,[[prototype]]可以用__proto__访问,即dog.__proto__ = Animal.prototype
class
没有class之前,生成实例都是用构造函数, es6中引入了class概念,可以像C++等直接用class关键字来定义类
e.g.
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 33 34 35 36
| class Animal { species = 'bird'; constructor(name, age) { this.name = name; this.age = age; this.Height = 100; } speak() { return this.name + ', speak'; } static voice() { return 'this is my voice'; } get height() { return this.Height * 3; } set height(val) { this.Height += val; } } const cat = new Animal('Tom', 18); console.log(cat, cat.speak(), Animal.voice());
Animal { name: 'Tom', age: 18, Height: 100 } cat.__proto__= { constructor: f Aniaml(name, age), speak: f speak() {}, height: 300, get height: f() {}, set height: f(val) {}, __proto__: Object }
|
构造函数式继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function Person(name, age) { this.name = name; this.age = age; this.hand = 'hand'; }
function Child(name, age) { Person.call(this, name, age); } const tom = new Child('Tom', 10);
{ name = 'Tom'; age = 10; hand = 'hand'; }
|
原型链式继承
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
| function Animal() { this.action = 'move'; this.speak = 'speak'; } Animal.prototype.hasLeg = 'true';
function Cat() { this.name = 'cat'; } Cat.prototype = new Animal(); Cat.prototype.height = 50;
const tom = new Cat();
{ name: 'cat', __proto__: Animal }
{ action: "move", height: 50, speak: "speak", __proto__: Object }
{ hasleg: 'true', constructor: ƒ Animal(), __proto__: Object }
|
class继承
class的继承有专门的语法
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
| class Animal { speak = 'bar'; constructor() { this.name = 'Animal'; this.age = 15; this.Height = 100; } } class Dog extends Animal { constructor(...args) { super(...args); this.speak = 'bar'; } dogSpeak() { return super.speak(); } }
console.log(dog);
|
class的继承是将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。也就是只有先调用super,才能够使用this.
注意:super
当方法super()使用时,会继承父类的构造函数,只能在子类的构造函数中使用;
当对象super.XXX使用时,指向父类的原型对象Animal.prototype;
如果super作为对象,用在静态方法之中,这时super将指向父类;
在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例
参考资料
《ECMAScript 6入门》– 阮一峰