在 javascript 中,使用构造函数实现继承的核心做法是结合原型链(prototype chain)与构造函数绑定(constructor binding),即通过 object.create() 建立原型关系,并利用 call() 或 apply() 在子类构造函数内部调用父类构造函数以初始化实例属性。

如何正确设置子类的原型对象
要让子类实例能够访问父类的方法,必须将子类的原型对象指向父类的一个实例。推荐使用 Object.create(Parent.prototype) 来创建这个原型对象,这样可以避免直接赋值导致父类实例被意外共享或污染。设置完成后,还需要手动修正 constructor 属性,使其重新指向子类构造函数,以保持元数据的准确性。

function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name, age) {
// 调用父类构造函数,初始化实例属性
Parent.call(this, name);
this.age = age;
}
// 设置原型链
Child.prototype = Object.create(Parent.prototype);
// 修正 constructor 指向
Child.prototype.constructor = Child;
Child.prototype.sayAge = function() {
console.log(this.age);
};
const child = new Child('Alice', 10);
child.sayName(); // 输出: Alice
child.sayAge(); // 输出: 10
如何在子类中复用父类的实例属性
在子类构造函数内部,通过 Parent.call(this, ...args) 的方式执行父类构造函数。这里的 this 指向当前正在创建的子类实例,因此父类中定义在 this 上的属性会被直接添加到子类实例上。这种方式确保了每个子类实例都拥有独立的属性副本,避免了引用类型的共享问题。
立即学习“Java免费学习笔记(深入)”;

// 对比:如果不用 call,而是直接赋值
// Child.prototype = new Parent();
// 这会导致所有 Child 实例共享同一个 Parent 实例的属性,且无法向 Parent 传递参数。
// 使用 call 的正确方式
function Child(name, age) {
Parent.call(this, name); // 此时 this 是 Child 的实例
this.age = age;
}
当父类属性包含引用类型时会发生什么
如果使用 Parent.call(this) 的方式,每次创建子类实例时都会独立执行一次父类构造函数。这意味着即使父类属性中包含数组或对象等引用类型,每个子类实例也会获得该引用类型的独立副本,从而避免了数据污染。这是构造函数绑定模式相比纯原型链继承最大的优势之一。


















