使用原型链可以让多个对象共享相同的方法,从而节省内存。但这种方法也有一些问题,特别是当原型中包含引用类型的值时,这些值会在所有实例间共享。此外,子类型在实例化时不能给父类型的构造函数传参。
原型链的问题
- 引用类型共享问题:当原型中包含引用类型的值(如数组或对象)时,这些值会在所有实例间共享,修改一个实例上的引用类型属性,会影响所有其他实例。
- 无法传递参数:在使用原型链实现继承时,子类型在实例化时无法给父类型的构造函数传参,让父类型的初始化变得困难。
解决方案:盗用构造函数
可以使用“盗用构造函数”(constructor stealing)的技术。这种技术通过在子类型构造函数中调用父类型构造函数,并使用 apply
或 call
方法将父类型的构造函数的上下文设置为子类型的实例,从而解决引用类型共享和无法传参的问题。
具体用法
使用“盗用构造函数”的步骤如下:
- 在子类型的构造函数中调用父类型的构造函数,并使用
apply
或call
方法将父类型构造函数的this
指向子类型实例。 - 继续在子类型构造函数中定义子类型特有的属性。
以下是具体示例代码:
// 父类型构造函数
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
// 父类型的方法
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);
Child.prototype.constructor = Child;
// 子类型的方法
Child.prototype.sayAge = function() {
console.log(this.age);
};
// 创建实例并测试
let child1 = new Child("Alice", 5);
let child2 = new Child("Bob", 10);
child1.colors.push("yellow");
console.log(child1.colors); // ["red", "blue", "green", "yellow"]
console.log(child2.colors); // ["red", "blue", "green"]
child1.sayName(); // Alice
child1.sayAge(); // 5
child2.sayName(); // Bob
child2.sayAge(); // 10
在以上示例中,Child
构造函数通过 Parent.call(this, name)
调用了 Parent
构造函数,将 Parent
构造函数的 this
指向 Child
实例,从而解决了引用类型共享和无法传参的问题。通过 Object.create
方法,Child
的原型设置为 Parent
的实例,继承了 Parent
的方法。
这种方法即实现了属性的独立性,又保留了方法的共享性,可以用来解决原型链继承的问题。