JavaScript中的继承方式详解
字数 797 2025-11-05 23:47:39
JavaScript中的继承方式详解
描述
JavaScript中的继承是指一个对象能够访问另一个对象的属性和方法。由于JavaScript基于原型,继承主要通过原型链实现。ES5有几种经典继承方式,ES6引入了class语法糖。理解不同继承方式有助于编写可维护的面向对象代码。
逐步讲解
-
原型链继承
- 原理:将子类的原型对象指向父类的实例,从而通过原型链访问父类属性。
- 示例:
function Parent() { this.name = 'Parent'; } Parent.prototype.sayName = function() { console.log(this.name); }; function Child() {} Child.prototype = new Parent(); // 核心:原型指向父类实例 const child = new Child(); child.sayName(); // 输出"Parent",通过原型链访问 - 缺点:
- 所有子类实例共享父类引用属性(如数组),修改会相互影响。
- 无法向父类构造函数传参。
-
构造函数继承
- 原理:在子类构造函数中调用父类构造函数,通过
call或apply改变this指向。 - 示例:
function Parent(name) { this.name = name; this.colors = ['red']; } function Child(name) { Parent.call(this, name); // 核心:在子类上下文中执行父类构造函数 } const child1 = new Child('Child1'); child1.colors.push('blue'); console.log(child1.colors); // ['red', 'blue'](独立属性) const child2 = new Child('Child2'); console.log(child2.colors); // ['red'](不受child1影响) - 缺点:无法继承父类原型上的方法。
- 原理:在子类构造函数中调用父类构造函数,通过
-
组合继承
- 原理:结合原型链继承和构造函数继承,既继承原型方法又保证实例属性独立。
- 步骤:
- 使用构造函数继承父类实例属性。
- 使用原型链继承父类原型方法。
- 示例:
function Parent(name) { this.name = name; } Parent.prototype.sayName = function() { console.log(this.name); }; function Child(name, age) { Parent.call(this, name); // 第1次调用Parent:继承实例属性 this.age = age; } Child.prototype = new Parent(); // 第2次调用Parent:继承原型方法 Child.prototype.constructor = Child; // 修复constructor指向 const child = new Child('Tom', 10); child.sayName(); // "Tom" - 缺点:父类构造函数被调用两次(性能浪费)。
-
原型式继承
- 原理:基于现有对象创建新对象,ES5的
Object.create()方法实现此模式。 - 示例:
const parent = { name: 'Parent', friends: ['Alice'] }; const child1 = Object.create(parent); // 以parent为原型创建对象 child1.name = 'Child1'; child1.friends.push('Bob'); const child2 = Object.create(parent); console.log(child2.friends); // ['Alice', 'Bob'](共享引用属性) - 适用场景:简单对象继承,无需构造函数。
- 原理:基于现有对象创建新对象,ES5的
-
寄生组合继承
- 原理:优化组合继承,避免两次调用父类构造函数。通过中间对象连接子类和父类原型。
- 步骤:
- 使用构造函数继承实例属性。
- 创建一个空函数,将其原型指向父类原型。
- 将子类原型指向空函数的实例。
- 示例:
function inheritPrototype(Child, Parent) { const F = function() {}; // 空构造函数 F.prototype = Parent.prototype; Child.prototype = new F(); // 仅继承原型链,不调用父类构造函数 Child.prototype.constructor = Child; } function Parent(name) { this.name = name; } function Child(name, age) { Parent.call(this, name); // 仅1次构造函数调用 this.age = age; } inheritPrototype(Child, Parent); - 优点:ES6以前最完美的继承方式。
-
ES6的class继承
- 原理:
class和extends是语法糖,底层仍基于原型链,通过super调用父类。 - 示例:
class Parent { constructor(name) { this.name = name; } sayName() { console.log(this.name); } } class Child extends Parent { constructor(name, age) { super(name); // 相当于Parent.call(this, name) this.age = age; } } const child = new Child('Tom', 10); child.sayName(); // "Tom" - 注意:必须先调用
super()才能使用this。
- 原理:
总结
- 优先使用ES6的class继承,简洁且无历史包袱。
- 理解底层原型链机制有助于调试复杂继承关系。
- 寄生组合继承是理解class继承原理的关键。