call借用构造函数是为了让子类实例拥有父类通过this初始化的独立属性,避免共享;必须用call而非new或直接调用,因其能精准绑定this到子类实例执行初始化逻辑。

使用 call 借用构造函数,是为了让子类实例能拥有父类构造函数中通过 this 初始化的属性(比如实例私有属性、原始值或独立引用类型),避免共享问题。它不是“完美克隆”,而是“独立继承初始化逻辑”——关键在执行时绑定新实例上下文。
为什么必须用 call 而不是 new 或直接调用?
父类构造函数通常依赖 this 绑定到当前实例来设置属性。若直接调用(如 Parent()),this 指向全局或 undefined(严格模式),属性会挂错地方;若用 new Parent(),则创建了多余中间实例,且无法与子类实例合并。只有 Parent.call(this, ...args) 能精准将初始化逻辑“重放”到子类实例上。
在寄生组合式继承中正确调用的位置
寄生组合式继承 = 原型链继承方法 + 构造函数继承属性。call 必须放在子类构造函数内部、super 之后(ES6 class)或显式初始化前(ES5):
- ES6 示例:
class Child extends Parent {
constructor(...args) {
super(...args); // 先确保父类 this 初始化(可选,但推荐)
Parent.call(this, ...args); // 关键:重新运行父类初始化逻辑,覆盖/补充实例属性
}
} - ES5 示例:
function Child(name, age) {
Parent.call(this, name); // 在这里调用,this 指向新创建的 Child 实例
this.age = age;
}
注意属性覆盖与重复初始化风险
Parent.call(this) 会再次执行父类构造函数体,可能覆盖已有属性或造成副作用(如重复计数、重复绑定事件)。应确保父类构造函数是“纯初始化”的,或在子类中控制入参:
- 避免在父类构造函数中做 DOM 操作、发请求等副作用
- 若父类已通过
super()初始化,再call同一参数可能导致属性被写两次(如this.name被赋值两次) - 真正需要的是“只继承特异属性”,那就只传必要参数,甚至拆分父类逻辑:把可复用的初始化抽成独立方法,子类按需调用
配合原型链,才能完整继承
call 只解决实例属性,方法仍需原型继承。务必同步设置原型关系:
- ES6:
extends自动处理,无需额外操作 - ES5:用
Object.setPrototypeOf(Child.prototype, Parent.prototype)或Child.prototype = Object.create(Parent.prototype),再修复constructor - 不设原型链 → 子类实例无法访问父类原型方法;只设原型链不
call→ 实例缺少this.xxx属性











