构造函数窃取是通过在子类构造函数中调用父类构造函数,将父类的实例属性绑定到子类实例上的一种继承方式。具体做法是在子类函数内部使用 parent.call(this, ...) 或 reflect.construct 来执行父类逻辑,从而让子类实例拥有父类的私有属性和方法。这种方式解决了原型链继承无法向父类构造函数传参的问题,但需要配合原型链设置来实现方法的共享。

为什么原型链继承无法解决传参问题
原型链继承通过 Child.prototype = new Parent() 实现,这会导致所有子类实例共享同一个父类实例对象。当父类构造函数包含引用类型属性(如数组)时,修改一个实例的属性会影响其他实例。更关键的是,创建子类实例时无法向父类构造函数传递特定的参数,因为父类构造函数在设置原型时就已经被固定调用了。

function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
function Child(name, age) {
// 无法在这里向 Parent 传递 name
Parent.call(this, name); // 构造函数窃取
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const child1 = new Child('Alice', 10);
const child2 = new Child('Bob', 12);
console.log(child1.name); // Alice
console.log(child1.colors); // ['red', 'blue']
上述代码展示了标准组合模式。首先通过 Parent.call(this, name) 完成属性窃取,确保每个实例拥有独立的 name 和 colors。接着通过 Object.create 设置原型链,确保方法共享。运行结果显示,child1 正确获取了传入的姓名,且拥有独立的颜色数组。
构造函数窃取如何处理多个父类
当子类需要继承多个父类的属性时,可以多次调用不同父类的构造函数。每次调用都使用 .call() 或 .apply() 并将 this 指向当前子类实例。这样可以合并多个父类的属性,但需要注意属性名冲突的处理,后调用的父类属性会覆盖先调用的同名属性。

function ParentA() {
this.type = 'A';
}
function ParentB() {
this.type = 'B';
this.color = 'red';
}
function Child(name) {
ParentA.call(this);
ParentB.call(this, name); // 注意:ParentB 可能需要参数
this.name = name;
}
Child.prototype = Object.create(ParentA.prototype);
// 需要手动合并原型方法或分别链接
const child = new Child('Test');
console.log(child.type); // B (被 ParentB 覆盖)
console.log(child.color); // red
该示例演示了多父类继承的场景。ParentA.call(this) 首先注入 type: 'A',随后 ParentB.call(this) 注入 type: 'B' 和 color: 'red'。最终实例的 type 属性值为 'B',体现了后调用者覆盖前调用者的行为。这种模式允许灵活组合功能,但需开发者显式管理原型链和方法继承。
构造函数窃取对原型方法的影响
构造函数窃取仅复制实例属性,不会自动继承父类原型上的方法。如果只使用 Parent.call(this) 而不设置原型链,子类实例将无法访问父类定义在原型上的方法。因此,必须结合原型链继承或 Object.setPrototypeOf 来补全方法继承,形成完整的继承体系。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











