在JavaScript中,这两种设置子类(Child
)原型(prototype
)的方法虽然都能实现继承的基本功能,但它们之间存在一些重要的区别和潜在的陷阱。
1. Child.prototype = Object.create(Parent.prototype);
这个方法使用Object.create()
来创建一个新对象,其原型(__proto__
)被设置为Parent.prototype
。这样做的结果是,Child.prototype
会继承Parent.prototype
上的所有属性和方法(注意,这里指的是原型链上的继承,而非实例属性或方法的继承),但不包括Parent
构造函数内部可能初始化的任何实例属性或方法。
优点:
- 清晰地区分了原型链继承和构造函数实例初始化。
- 避免了不必要的实例属性或方法被继承(即,只继承了原型上的属性和方法)。
- 更符合原型链继承的初衷和原理。
示例:
function Parent() {
this.name = 'Parent';
}
Parent.prototype.sayHello = function() {
console.log('Hello from Parent!');
};
function Child() {
this.age = 10;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child; // 需要手动修复constructor属性
var child = new Child();
console.log(child.sayHello()); // Hello from Parent!
console.log(child.name); // undefined,因为name是Parent实例的属性,不是原型属性
下面通过代码解释一下 Object.create的实现流程
#详解 Object.create 实现逻辑
function create(proto) {
function F(){}
F.prototype = proto
return new F()
}
2. Child.prototype = new Parent();
这个方法通过创建一个Parent
的新实例,并将其赋值给Child.prototype
。这会导致Child.prototype
不仅继承了Parent.prototype
上的所有属性和方法,还继承了Parent
构造函数内部初始化的任何实例属性或方法(如果有的话)。
缺点:
- 如果
Parent
构造函数内部有对实例属性的初始化(如上例中的this.name = 'Parent';
),这些实例属性也会被继承到Child.prototype
上,这可能不是你想要的结果,因为原型上的属性或方法应该是被所有实例共享的,而不是每个实例都应该有自己的拷贝。 - 可能导致不必要的内存消耗,因为每个通过
Child
构造函数创建的实例都会间接地包含一个Parent
实例的所有实例属性(尽管它们通常不会被访问或使用)。
示例(注意,这里展示了潜在的问题):
function Parent() {
this.name = 'Parent'; // 这会被继承到Child.prototype上,通常不是期望的
}
Parent.prototype.sayHello = function() {
console.log('Hello from Parent!');
};
function Child() {
this.age = 10;
}
Child.prototype = new Parent(); // 这将'name'属性也带到了Child.prototype上
Child.prototype.constructor = Child; // 需要手动修复constructor属性
var child = new Child();
console.log(child.sayHello()); // Hello from Parent!
console.log(child.name); // 'Parent',这不是我们想要的,因为name应该是实例属性
console.log(Child.prototype.name); // 'Parent',这证明了name被继承到了Child.prototype上
综上所述,Object.create(Parent.prototype)
是更推荐的方式,因为它更清晰、更准确地实现了原型链继承,避免了不必要的实例属性被继承到原型上。