📚 简介
JavaScript 继承机制是前端开发中的核心概念。本文将详细介绍 8 种常见的继承方式,包括它们的优缺点和适用场景。
1️⃣ 原型链继承(Prototype Chain)
特点
- ✅ 优点:实现简单,可共享父类原型上的方法
- ❌ 缺点:所有子实例共享引用类型属性,无法向父构造函数传参,无法实现多继承
- 🎯 适用场景:仅共享方法且没有引用类型状态时
代码示例
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 Parent() { this.age = 40; this.colors = ["red"]; }
Parent.prototype.say = function () { console.log(this.age); };
Parent.prototype.appendColor = function (color) { this.colors.push(color); console.log("原型链继承:", this.colors); };
function Child() {}
Child.prototype = new Parent(); Child.prototype.constructor = Child;
const child1 = new Child(); child1.say(); console.log(child1.age); child1.appendColor("blue");
const child2 = new Child(); child2.appendColor("green");
|
2️⃣ 借用构造函数继承(Constructor Stealing)
特点
- ✅ 优点:每个实例有自己的实例属性(避免共享引用),可以向父构造函数传参
- ❌ 缺点:父原型上的方法不能继承,每个子实例都会创建新的方法副本(效率差)
- 🎯 适用场景:需要继承实例属性且不关心父原型方法共享时
代码示例
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
| function Parent(name) { this.name = name; this.colors = ["red"];
this.sayName = function () { console.log("借用构造函数:", this.name); }; }
Parent.prototype.sayPrototype = function () { console.log("原型方法:", this.name); };
function Child(name) { Parent.call(this, name); }
const child1 = new Child("Alice"); child1.sayName();
const child2 = new Child("Bob"); console.log(child2.sayName === child1.sayName);
|
3️⃣ 组合继承(Combination Inheritance)
特点
- ✅ 优点:结合了原型链和借用构造函数的优点,实例有自己的属性,又能共享方法
- ❌ 缺点:调用了两次父构造函数,造成性能开销
- 🎯 适用场景:早期常用方案,需要继承实例属性和共享方法时
代码示例
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
| function Parent() { this.name = "parent"; this.colors = ["red"]; console.log("Parent 构造函数被调用"); }
Parent.prototype.say = function () { console.log("组合继承:", this.name); };
function Child(name, key) { Parent.call(this); this.name = name; this.key = key; }
Child.prototype = new Parent(); Child.prototype.constructor = Child;
const child1 = new Child("Alice", "key1"); const child2 = new Child("Bob", "key2");
console.log(child1.say === child2.say); console.log(child1.key, child2.key);
|
4️⃣ 函数式继承(Functional Inheritance)
特点
- ✅ 优点:封装强、无原型链副作用,适合模块化
- ❌ 缺点:方法无法共享到原型,内存开销较大
- 🎯 适用场景:需要强封装和私有变量时
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function createPerson(name) { return { say() { console.log("函数式继承:", name); }, }; }
const person1 = createPerson("Alice"); person1.say();
const person2 = createPerson("Bob"); person2.say();
console.log(person1.say === person2.say);
|
5️⃣ 寄生式继承(Parasitic Inheritance)
特点
- ✅ 优点:灵活,适合创建带增强功能的对象
- ❌ 缺点:方法不能共享,效率不高
- 🎯 适用场景:需要在对象基础上添加额外方法时
代码示例
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
| function createEnhancedObject(original) { const clone = Object.create(original);
clone.sayHi = function () { console.log("寄生式继承:", this.name); };
return clone; }
const original = { name: "original", colors: ["red"], };
const enhanced1 = createEnhancedObject(original); enhanced1.sayHi();
const enhanced2 = createEnhancedObject(original); enhanced2.sayHi();
console.log(enhanced1.sayHi === enhanced2.sayHi);
|
6️⃣ 寄生组合继承(Parasitic Combination)⭐
特点
- ✅ 优点:最优的 ES5 继承方案,避免双构造调用,保留实例属性和原型方法共享
- ❌ 缺点:实现相对复杂
- 🎯 适用场景:ES5 环境下的推荐方案
代码示例
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 37 38
| function inheritPrototype(Child, Parent) { Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; }
function Parent(name) { this.name = name; console.log("Parent 构造函数被调用"); }
Parent.prototype.say = function () { console.log("寄生组合继承:", this.name); };
function Child(name, age) { Parent.call(this, name); this.age = age; }
inheritPrototype(Child, Parent);
const child1 = new Child("Alice", 10); const child2 = new Child("Bob", 20);
child1.say(); child2.say();
console.log(child1.say === child2.say); console.log(child1.age, child2.age);
|
7️⃣ ES6 Class 继承 ⭐⭐
特点
- ✅ 优点:语法简洁,符合现代 JavaScript 习惯,自动调用
super() 和设置原型
- ❌ 缺点:不支持低版本浏览器,仍然是基于原型的语法糖
- 🎯 适用场景:现代 JavaScript 项目的首选方案
代码示例
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
| class Parent { constructor(name) { this.name = name; }
say() { console.log("ES6 类继承:", this.name); } }
class Child extends Parent { constructor(name, age) { super(name); this.age = age; } }
const child1 = new Child("Alice", 10); child1.say();
const child2 = new Child("Bob", 20); console.log(child1.say === child2.say); console.log(child1.age, child2.age);
|
8️⃣ Mixin 混入
特点
- ✅ 优点:简单灵活,避免深层继承树,符合”组合优于继承”原则
- ❌ 缺点:方法是拷贝不是继承,可能有命名冲突
- 🎯 适用场景:需要多重继承或组合多个功能时
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const Eater = { eat() { console.log("Mixin: eating..."); }, };
const Walker = { walk() { console.log("Mixin: walking..."); }, };
const person = Object.assign({}, Eater, Walker);
person.eat(); person.walk();
|
高级用法:函数式 Mixin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const flyMixin = (target) => { target.fly = function () { console.log("Flying..."); }; return target; };
const swimMixin = (target) => { target.swim = function () { console.log("Swimming..."); }; return target; };
class Animal {} const duck = new Animal();
flyMixin(duck); swimMixin(duck);
duck.fly(); duck.swim();
|
📊 继承方式对比
| 继承方式 |
属性继承 |
方法共享 |
构造函数调用次数 |
推荐指数 |
| 原型链继承 |
❌ 共享 |
✅ |
1 次 |
⭐⭐ |
| 借用构造函数 |
✅ |
❌ |
1 次 |
⭐⭐ |
| 组合继承 |
✅ |
✅ |
2 次 |
⭐⭐⭐ |
| 函数式继承 |
✅ |
❌ |
0 次 |
⭐⭐ |
| 寄生式继承 |
✅ |
❌ |
0 次 |
⭐⭐ |
| 寄生组合继承 |
✅ |
✅ |
1 次 |
⭐⭐⭐⭐ |
| ES6 Class |
✅ |
✅ |
1 次 |
⭐⭐⭐⭐⭐ |
| Mixin |
✅ |
✅ |
0 次 |
⭐⭐⭐⭐ |
🎯 选择建议
- 现代项目:优先使用
ES6 Class 继承
- 需要多重继承:使用
Mixin 组合功能
- ES5 环境:使用
寄生组合继承
- 简单场景:使用
组合继承
💡 总结
- JavaScript 继承本质上是基于原型链的
- ES6 Class 只是语法糖,底层仍然使用原型
- 组合优于继承 —— 在可能的情况下,优先使用 Mixin 而不是深层继承链
- 理解继承机制对于掌握 JavaScript 面向对象编程至关重要 ✨