js继承in-ES6-ES5
ES5 与 ES6 继承写法对比,底层都回到原型链。
#type / concept
#status / growing
#resource / javascript
#resource / ecmascript
[!info] related notes
- 所属 MOC: ecmascript-object-oriented, ES6 新特性 MOC
- 前置概念: js-class, js原型和原型链, js构造函数
- 关系笔记: ecmascript-object-oriented
ES5 继承 vs ES6 class extends
一句话定义
ES5 通过原型链 + call/apply 借用构造函数实现继承;ES6 用 class extends + super 提供语法糖,底层机制完全相同——都是原型链连接。
核心机制 / 工作原理
ES5 方式一:原型链继承
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function () {
return this.name + ' makes a noise.';
};
function Dog(name) {
Animal.call(this, name); // 借用父构造函数,继承实例属性
}
Dog.prototype = Object.create(Animal.prototype); // 原型链连接
Dog.prototype.constructor = Dog; // 修复 constructor 指向
Dog.prototype.bark = function () {
return this.name + ' barks!';
};
关键步骤:
Animal.call(this, name)— 借用构造函数,把父类实例属性复制到子类实例Object.create(Animal.prototype)— 创建以父原型为原型的新对象,建立原型链- 修复
constructor指向
ES5 方式二:组合继承(最常用)
上面的写法就是组合继承 = 借用构造函数 + 原型链。解决了:
- 借用构造函数只能继承实例属性,不能继承原型方法
- 原型链继承时,引用类型的属性会被所有实例共享
ES6 class extends
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return this.name + ' makes a noise.';
}
}
class Dog extends Animal {
constructor(name) {
super(name); // 必须先调用 super,才能使用 this
}
bark() {
return this.name + ' barks!';
}
}
class 是语法糖
ES6 的 class 完全等价于 ES5 的原型链写法:
class中的方法定义在Constructor.prototype上super()等价于Animal.call(this, name)extends等价于Dog.prototype = Object.create(Animal.prototype)- 静态方法定义在
Constructor自身上
最小例子
// ES5
function Shape(color) {
this.color = color;
}
Shape.prototype.describe = function () {
return 'A ' + this.color + ' shape';
};
function Circle(color, radius) {
Shape.call(this, color);
this.radius = radius;
}
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;
Circle.prototype.area = function () {
return Math.PI * this.radius ** 2;
};
// ES6 —— 同样的效果
class Shape {
constructor(color) { this.color = color; }
describe() { return 'A ' + this.color + ' shape'; }
}
class Circle extends Shape {
constructor(color, radius) {
super(color);
this.radius = radius;
}
area() { return Math.PI * this.radius ** 2; }
}
边界与常见误解
- “ES6 class 是新的继承机制” — 不是。
class是原型链的语法糖,底层没有变化。instanceof和原型链遍历完全一致。 - “super 是调用父类” —
super()调用父构造函数;super.method()调用父原型上的方法。两者不同。 - 忘记调用
super— 在子类constructor中,必须先调用super()才能使用this,否则 ReferenceError。 - ES5 原型链继承的坑 — 如果不
Object.create而直接Dog.prototype = new Animal(),会在原型对象上执行构造函数,产生副作用。 __proto__vsprototype— 子类__proto__指向父类(静态方法继承),子类prototype.__proto__指向父类prototype(实例方法继承)。