推荐答案
// www.javascriptcn.com code example
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)
}
const child1 = new Child('Alice', 10);
const child2 = new Child('Bob', 12);
child1.colors.push('yellow');
console.log(child1.name); // 输出: Alice
child1.sayName(); // 输出: Alice
console.log(child1.colors); // 输出: ["red", "blue", "green", "yellow"]
child1.sayAge(); // 输出: 10
console.log(child2.name); // 输出: Bob
child2.sayName(); // 输出: Bob
console.log(child2.colors); // 输出: ["red", "blue", "green"]
child2.sayAge(); //输出: 12本题详细解读
原型链继承是 JavaScript 中实现继承的一种方式,它通过将子类型的原型设置为父类型的实例来实现继承。这样,子类型就可以访问父类型原型上的属性和方法。
核心思想:
让子类型的原型(Child.prototype)指向父类型的实例(Parent.prototype), 从而建立一条原型链,使得子类型实例能够访问父类型原型上的属性和方法。
实现步骤:
创建父类型构造函数: 定义一个父类型的构造函数
Parent,并在其原型上添加需要被继承的属性或方法。创建子类型构造函数: 定义一个子类型的构造函数
Child。借用构造函数继承属性: 在
Child构造函数内部,使用Parent.call(this, ...)来调用Parent构造函数,这样就可以在Child的实例中创建父类型的属性,确保父类型实例属性的隔离,避免共享。原型链继承方法: 使用
Child.prototype = Object.create(Parent.prototype);将子类型的原型Child.prototype设置为父类型原型Parent.prototype的一个新对象,这里使用Object.create()可以避免直接使用new Parent()而可能导致的额外属性。修正构造函数指向: 将
Child.prototype.constructor指向Child构造函数本身, 否则原型链继承后,构造函数会指向Parent。子类型添加特有方法: 可以在
Child.prototype上添加子类型特有的方法。
优点:
- 实现简单:原型链继承是JavaScript中最基本的继承方式,实现起来比较简单。
- 父类型原型上的方法可以被子类型共享:子类型实例可以访问父类型原型上的方法。
缺点:
引用类型共享问题: 通过原型链继承,父类型实例属性的引用类型会被子类型实例共享。例如,如果
Parent中有一个colors数组,多个Child实例会共享同一个colors数组,修改一个实例的colors会影响到其他实例。 这就是为什么在上述代码中,在Child构造函数内部使用了Parent.call(this, name);来借用构造函数,使得父类的属性变成子类的实例属性。创建子类型实例时,不能向父类型的构造函数传递参数: 因为
Child.prototype是被设置为Parent的实例的,这意味着,在执行Child.prototype = new Parent()时,Parent的构造函数会被执行,而这个Parent的执行没有办法接收任何参数。
改进方法:
通过 组合继承(借用构造函数+原型链继承),可以结合借用构造函数来实现父类型实例属性的隔离,并使用原型链继承父类原型上的方法,从而解决上述问题。上述推荐答案代码即展示了组合继承的实现方式。