ES5/ES6 的继承除了写法以外还有什么区别?
在 ES5 和 ES6 中,继承机制不仅仅在写法上有区别,还有一些底层实现和行为上的差异。以下是 ES5 和 ES6 继承的主要区别:
1. 原型链的构建方式不同
ES5
- 在 ES5 中,继承通常通过
prototype
和call
或apply
方法来模拟。具体步骤包括将子类的prototype
指向父类的实例,这样子类就继承了父类的所有属性和方法。 - 为了确保子类构造函数的正确性,还需要手动调整
constructor
指向。
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name); // 调用父类构造函数,继承属性
this.age = age;
}
// 继承父类的方法
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.sayAge = function() {
console.log(this.age);
};
var child = new Child('John', 18);
child.sayName(); // 输出: John
child.sayAge(); // 输出: 18
在 ES5 中, Parent.call(this, name)
是为了在子类构造函数中调用父类构造函数,确保父类的属性能够正确地初始化。然后,通过 Object.create(Parent.prototype)
继承父类的方法和属性。
ES6
- 在 ES6 中,使用
class
和extends
关键字来简化继承操作。子类通过extends
关键字继承父类,super
关键字用于调用父类的构造函数和方法。 - ES6 的继承机制与 ES5 的不同点在于,ES6 的子类会先初始化父类,随后再初始化子类的自身属性。
class Parent {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // 调用父类构造函数,继承属性
this.age = age;
}
sayAge() {
console.log(this.age);
}
}
const child = new Child('John', 18);
child.sayName(); // 输出: John
child.sayAge(); // 输出: 18
在 ES6 中, super(name)
是必须的,因为 ES6 子类的构造函数中,必须先调用 super()
,才能使用 this
。这是因为在 ES6 中,子类的构造函数默认是空的,只有在调用了父类的构造函数后,子类才能完成自身的初始化。
2. 子类 this
的处理
ES5
- 在 ES5 中,
this
是先创建子类实例,再通过Parent.call(this)
初始化父类的属性。
ES6
- 在 ES6 中,
this
的生成和绑定发生在调用super()
之后。换句话说,super()
调用之前不能使用this
,否则会抛出错误。 - ES6 中,
super()
的调用顺序决定了子类实例的初始化顺序,必须先调用父类构造函数来创建子类实例的this
对象。
class Parent {
constructor() {
console.log('Parent constructor');
}
}
class Child extends Parent {
constructor() {
// 如果此处不调用 super(),会报错
console.log(this); // ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
super();
console.log('Child constructor');
}
}
const child = new Child();
3. 内建对象的继承
ES5
- 在 ES5 中,直接继承内建对象(如
Array
、Error
等)是困难的。因为这些内建对象的构造函数在 ES5 中并不会返回正确的继承结果。
function MyArray() {
Array.call(this);
}
MyArray.prototype = Object.create(Array.prototype);
MyArray.prototype.constructor = MyArray;
var arr = new MyArray();
arr.push(1); // 在 ES5 中,这样做并不会正常工作
ES6
- 在 ES6 中,通过
class
和extends
,可以轻松地继承内建对象。ES6 继承机制内部处理了继承内建对象时的一些复杂性,确保子类正确地继承内建对象的方法和行为。
class MyArray extends Array {
constructor(...args) {
super(...args);
}
}
const arr = new MyArray();
arr.push(1);
console.log(arr); // [1],在 ES6 中可以正常工作
4. 静态方法的继承
ES5
- 在 ES5 中,静态方法需要手动继承。
function Parent() {}
Parent.staticMethod = function() {
console.log('Parent static method');
};
function Child() {}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
// 手动继承静态方法
Child.staticMethod = Parent.staticMethod;
Child.staticMethod(); // 输出: Parent static method
ES6
- 在 ES6 中,静态方法通过
extends
关键字自动继承,无需手动处理。
class Parent {
static staticMethod() {
console.log('Parent static method');
}
}
class Child extends Parent {}
Child.staticMethod(); // 输出: Parent static method
5. 在引擎层面的优化
- ES5 :由于 ES5 的继承是通过函数、原型链和手动管理
constructor
来实现,开发者需要显式处理继承细节,如原型链的连接、属性的初始化等,这种实现方式可能在某些场景下会导致性能开销较大。 - ES6 :ES6 的继承机制是基于更现代的
class
语法和super
关键字,JavaScript 引擎对其进行了优化,因此在某些情况下,ES6 的继承性能可能会优于 ES5。
总结
- 写法上 :ES6 的继承写法更简洁、直观,使用了
class
、extends
和super
等关键字,而 ES5 需要手动操作原型链和构造函数。 - this 绑定 :在 ES6 中,
this
的绑定依赖于super()
的调用,而 ES5 中this
的绑定发生在构造函数调用时。 - 内建对象继承 :ES6 对内建对象的继承支持更好。
- 静态方法继承 :ES6 中静态方法继承更加自动化。
- 性能和优化 :ES6 在底层引擎层面对
class
继承进行了优化,可能在某些场景下比 ES5 继承更高效。
总体来说,ES6 的继承机制更符合面向对象编程的概念,并简化了代码书写和维护。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论