优点:简单易实现;父类方法可以被多个子类实例共享,节省内存。
缺点:
例子:如果父类有一个数组属性,所有子类实例都会共享这个数组,一个实例对数组的修改会反映在所有实例上。
function Parent() {
this.colors = ['red', 'blue', 'green'];
}
function Child() {}
Child.prototype = new Parent();
var child1 = new Child();
child1.colors.push('black');
var child2 = new Child();
console.log(child2.colors); // ['red', 'blue', 'green', 'black']
子类实例不能向父类构造函数传参
```javascript
function Parent(name) {
this.name = name;
}
function Child() {}
Child.prototype = new Parent();
var child = new Child("Child Name");
console.log(child.name); // 输出: undefined
```
例子:每个子类实例都会创建自己的 sayName
方法,导致无法共享同一个函数。
function Parent(name) {
this.name = name;
this.sayName = function() {
console.log(this.name);
};
}
function Child(name) {
Parent.call(this, name);
}
var child1 = new Child('child1');
var child2 = new Child('child2');
console.log(child1.sayName === child2.sayName); // false
例子:Parent
构造函数被调用两次,一次是在创建 Child.prototype
,另一次是在 Child
构造函数中。
function Parent(name) {
this.name = name;
}
function Child(name, age) {
Parent.call(this, name); // 第一次调用 Parent
this.age = age;
}
Child.prototype = new Parent(); // 第二次调用 Parent
// 父类
function Animal(name) {
this.name = name;
this.colors = ['white', 'black', 'brown'];
}
Animal.prototype.sayName = function() {
console.log(this.name);
};
// 子类
function Dog(name, breed) {
Animal.call(this, name); // 继承属性
this.breed = breed;
}
// 寄生组合式继承的核心函数
function inheritPrototype(childClass, parentClass) {
var prototype = Object.create(parentClass.prototype); // 创建对象
prototype.constructor = childClass; // 增强对象
childClass.prototype = prototype; // 指定对象
}
// 应用寄生组合式继承
inheritPrototype(Dog, Animal);
// 新增或重写子类方法
Dog.prototype.sayBreed = function() {
console.log(this.breed);
};
// 测试寄生组合式继承
var myDog = new Dog("Rex", "Golden Retriever");
myDog.sayName(); // 输出 "Rex"
myDog.sayBreed(); // 输出 "Golden Retriever"
在JavaScript中,指定 prototype.constructor = childClass
是重要的一步,因为它确保了继承过程中保持了构造函数(constructor)的正确指向。这个步骤对于理解和维护对象的原型链是非常关键的。
当你使用 Object.create(superType.prototype)
创建一个新对象时,这个新对象的原型是 superType.prototype
。这意味着新对象(即子类的原型对象)的 constructor
属性是继承自 superType.prototype
的,因此它指向的是 superType
而不是 childType
。这在逻辑上是不正确的,因为我们希望 childType
的实例的构造器(constructor)指向 childType
本身。
通过设置 prototype.constructor = childClass
,我们确保了:
childClass
的实例都应该引用 childClass
作为其构造函数。以下是一个简化的例子,展示了为什么需要这个步骤:
function Parent() {}
function Child() {}
Child.prototype = Object.create(Parent.prototype);
console.log(Child.prototype.constructor === Parent); // true
// 在这一步,Child的原型的构造函数还指向Parent
Child.prototype.constructor = Child;
console.log(Child.prototype.constructor === Child); // true
// 现在我们更正了constructor属性,使其指向Child
在这个例子中,我们首先创建了一个新的对象,它的原型是 Parent.prototype
,但这使得 Child.prototype.constructor
错误地指向 Parent
。然后,我们将 Child.prototype.constructor
更正为 Child
,以反映实际的继承关系。这样,任何 Child
的实例都会正确地将其 constructor
属性指向 Child
。