Appearance
4. 常见的继承方式都有什么,分别有什么问题?
难度等级:🟡 中级 分类:JavaScript 归档日期:2026-10-20
一、原型链继承
最单纯的继承,就是把子类的原型对象设置为父类的一个实例。这样,子类实例既可以访问父类构造函数初始化在这个对象上的属性和方法,也可以沿着原型链访问父类原型上的方法。但是这种方式存在一些问题:
- 引用类型的属性被所有子类实例共享;
- 创建子类实例时,不能给父类构造函数传参;
- 执行
Child.prototype = new Parent()时,父构造函数会被调用一次,如果里面有副作用,可能会产生不可预期的影响; constructor指向不符合预期(指向了Parent而不是Child)。
二、寄生组合式继承
用「借用构造函数 + Object.create 建立原型关系」可以解决上面的问题:
javascript
function Parent(name) {
this.name = name;
this.foods = []; // 每个实例都有自己的数组
}
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,保持不可枚举
Object.defineProperty(Child.prototype, 'constructor', {
value: Child,
writable: true,
configurable: true,
enumerable: false,
});
// 子类自己的方法:放在设置原型之后
Child.prototype.sayAge = function () {
console.log(this.age);
};
// 使用
const a = new Child('小明', 18);
const b = new Child('小红', 20);
a.foods.push('苹果');
console.log(a.foods); // ['苹果']
console.log(b.foods); // []
a.sayName(); // 小明
a.sayAge(); // 18
console.log(a.constructor === Child); // true
console.log(a instanceof Child); // true
console.log(a instanceof Parent); // true