理解 JavaScript 中继承的基本概念及其在面向对象编程中的作用
掌握基于原型的继承实现方式
熟悉 ES6 class 语法下的类继承机制
了解 Mixin、Object.create()、Object.setPrototypeOf() 和工厂函数等不同继承模式的使用场景与优缺点

在面向对象编程(OOP)中,继承允许一个类(或对象)从另一个类(或对象)获取属性和方法。这种机制避免了重复代码,提高了代码复用性和可维护性。
在 JavaScript 中,继承主要通过 原型链(Prototype Chain) 实现。所有对象都通过其内部的 [[Prototype]] 链接指向另一个对象,从而可以访问父对象的属性和方法。
类比:就像孩子继承父母的特征一样,子对象可以“继承”父对象的行为和状态。
这是 JavaScript 最原始、最核心的继承方式,适用于构造函数风格的编程。
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function () {
console.log(`${this.name} makes a sound.`);
};
// 子类构造函数
function Dog(name) {
Animal.call(this, name); // 调用父类构造函数,继承实例属性
}
// 设置原型链:Dog 继承 Animal 的方法
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // 修复构造函数指向
// 为 Dog 添加特有方法
Dog.prototype.bark = function () {
console.log(`${this.name} barks: Woof!`);
};
const myDog = new Dog("Buddy");
myDog.speak(); // Buddy makes a sound.
myDog.bark(); // Buddy barks: Woof!关键点说明:
Animal.call(this, name):确保 Dog 实例拥有 name 属性。
Object.create(Animal.prototype):建立原型链,使 Dog.prototype 能访问 Animal.prototype 上的方法。
修复 constructor:避免 myDog.constructor 指向 Animal。
这是 ES5 时代最标准的手动继承写法。
ES6 引入了 class 语法糖,使继承更直观、更接近传统 OOP 语言。
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `My name is ${this.name}`;
}
}
class Dog extends Animal {
constructor(name) {
super(name); // 调用父类构造函数
}
}
const dog = new Dog('Pranjal');
console.log(dog.speak()); // My name is Pranjal特点:
使用 extends 关键字声明继承关系。
子类构造函数中必须调用 super() 才能使用 this。
自动建立原型链,无需手动设置 prototype。
语法简洁,可读性强,是现代 JavaScript 推荐的继承方式。
多源继承
JavaScript 不支持多重继承,但可通过 Mixin 模拟从多个来源“混合”方法。
const Speakable = {
speak() {
return `${this.name} speaks`;
}
};
const Walkable = {
walk() {
return `${this.name} walks`;
}
};
function Person(name) {
this.name = name;
}
// 将多个对象的方法合并到 Person.prototype
Object.assign(Person.prototype, Speakable, Walkable);
const person = new Person('Pranjal');
console.log(person.speak()); // Pranjal speaks
console.log(person.walk()); // Pranjal walks适用场景:
需要从多个独立模块复用行为(如日志、验证、序列化等)。
避免深层次继承带来的耦合问题。
注意:Mixin 是浅拷贝,若方法有冲突,后合并的会覆盖前者。
Object.create()Object.create(proto) 创建一个新对象,其原型为 proto,常用于对象字面量之间的继承。
const animal = {
name: 'Unknown',
age: 0,
introduce() {
return `My name is ${this.name}`;
}
};
const dog = Object.create(animal);
dog.name = 'Buddy';
console.log(dog.age); // 0(继承自 animal)
console.log(dog.introduce()); // My name is Buddy(this 指向 dog)优势:
无需构造函数,直接基于现有对象创建新对象。
适合轻量级、动态的对象扩展。
Object.setPrototypeOf()动态设置原型
该方法可在运行时修改对象的原型链。
const behavior = {
name: 'Pranjal'
};
const speaker = {
speak() {
return `${this.name} speaks`;
}
};
Object.setPrototypeOf(speaker, behavior);
console.log(speaker.speak()); // Pranjal speaks注意事项:
虽然灵活,但性能较差,且破坏引擎优化。
官方文档建议避免使用,仅在必要时用于调试或特殊场景。
工厂函数返回配置好的对象,虽不涉及原型链,但可实现类似继承的效果。
function createPerson(name) {
return {
name: name,
greet() {
return `Hello, my name is ${this.name}`;
}
};
}
const alice = createPerson('Alice');
const bob = createPerson('Bob');
console.log(alice.greet()); // Hello, my name is Alice
console.log(bob.greet()); // Hello, my name is Bob特点:
每个对象独立拥有方法(无共享原型),内存开销较大。
适合简单对象创建,无需复杂继承结构。
隐藏实现细节,提供清晰的创建接口。
JavaScript 的继承本质是基于原型链的,所有继承方式最终都依赖于此机制。
ES6 class + extends 是当前最推荐的继承写法,语义清晰且兼容性良好。
Mixin 模式可弥补 JavaScript 不支持多重继承的不足,适合功能组合。
避免滥用 Object.setPrototypeOf(),它会影响性能并降低代码可预测性。
工厂函数虽非真正继承,但在某些场景下更简洁、更安全。
在基于原型的继承中,为什么需要手动修复 Dog.prototype.constructor = Dog?如果不修复会有什么影响?
ES6 的 class 是否真的引入了“类”?它与传统面向对象语言(如 Java)中的类有何本质区别?
如果你正在开发一个需要同时具备“可飞行”、“可游泳”和“可奔跑”能力的角色系统,你会选择哪种继承方式?为什么?