JavaScript 原型链继承
原型链继承是通过将子类原型指向父类实例实现的,是最基本的 JavaScript 继承模式。
基本实现
JavaScript
// 父类
function Animal(name) {
this.name = name;
this.colors = ['white', 'black']; // 引用类型属性
}
Animal.prototype.sayName = function() {
console.log('My name is ' + this.name);
};
// 子类:原型链继承
function Dog(name, breed) {
this.breed = breed;
}
Dog.prototype = new Animal('Dog'); // 子类原型指向父类实例
Dog.prototype.constructor = Dog; // 修复 constructor
// 使用
const dog1 = new Dog('Buddy', 'Golden');
const dog2 = new Dog('Max', 'Labrador');
dog1.sayName(); // 'My name is Dog'
console.log(dog1.breed); // 'Golden'
继承原理
JavaScript
// 原型链结构
// Dog.prototype = new Animal('Dog')
// 意味着:
// dog.__proto__ → Dog.prototype(Animal实例) → Animal.prototype → Object.prototype
const dog = new Dog('Buddy', 'Golden');
console.log(dog.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
// dog 可以访问 Animal.prototype 上的方法
dog.sayName(); // 来自 Animal.prototype
引用类型共享问题
JavaScript
// 原型链继承的核心缺陷
function Animal(name) {
this.colors = ['white', 'black']; // 引用类型
}
Animal.prototype.sayName = function() {};
function Dog() {}
Dog.prototype = new Animal('Dog');
const dog1 = new Dog();
const dog2 = new Dog();
// 引用类型属性被所有实例共享
dog1.colors.push('brown');
console.log(dog2.colors); // ['white', 'black', 'brown'] // 被污染!
// 解决方案:在子类构造函数中调用父类构造函数
function Dog(name) {
Animal.call(this, name); // 借用构造函数(组合继承)
this.type = 'dog';
}
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
const dog1 = new Dog('Buddy');
const dog2 = new Dog('Max');
dog1.colors.push('brown');
console.log(dog2.colors); // ['white', 'black'] // 独立
无法向父类传参
JavaScript
// 原型链继承:子类原型创建时无法动态传参
function Animal(name) {
this.name = name;
}
function Dog() {}
Dog.prototype = new Animal(); // 无法根据 Dog 实例传 name
const dog = new Dog();
console.log(dog.name); // undefined 或固定值
// 改进:组合继承
function Dog(name, breed) {
Animal.call(this, name); // 第一次调用父类
this.breed = breed;
}
Dog.prototype = new Animal(); // 第二次调用父类(冗余)
Dog.prototype.constructor = Dog;
const dog = new Dog('Buddy', 'Golden');
console.log(dog.name); // 'Buddy'
console.log(dog.breed); // 'Golden'
方法重写
JavaScript
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log('Animal speaks');
};
function Dog(name) {
this.name = name;
}
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
// 子类重写父类方法
Dog.prototype.speak = function() {
console.log(this.name + ' barks');
};
// 添加子类特有方法
Dog.prototype.fetch = function() {
console.log(this.name + ' fetches');
};
const dog = new Dog('Buddy');
dog.speak(); // 'Buddy barks'(重写方法)
dog.fetch(); // 'Buddy fetches'(特有方法)
寄生组合式继承(最优方案)
JavaScript
// 解决组合继承的两次调用问题
function inherit(Child, Parent) {
// 创建中介原型,避免调用父类构造函数
const prototype = Object.create(Parent.prototype);
prototype.constructor = Child;
Child.prototype = prototype;
}
function Animal(name) {
this.name = name;
this.colors = ['white'];
}
Animal.prototype.speak = function() {
console.log(this.name + ' speaks');
};
function Dog(name, breed) {
Animal.call(this, name); // 只调用一次父类
this.breed = breed;
}
inherit(Dog, Animal); // 寄生组合继承
const dog1 = new Dog('Buddy', 'Golden');
const dog2 = new Dog('Max', 'Labrador');
dog1.colors.push('brown');
console.log(dog2.colors); // ['white'] // 独立
dog1.speak(); // 'Buddy speaks'
console.log(dog1 instanceof Dog); // true
console.log(dog1 instanceof Animal); // true
原型链继承对比
| 继承方式 | 引用类型共享 | 向父类传参 | 父类调用次数 |
|---|---|---|---|
| 原型链继承 | 有问题 | 不支持 | 1次 |
| 借用构造函数 | 无问题 | 支持 | 每次实例化 |
| 组合继承 | 无问题 | 支持 | 2次(冗余) |
| 寄生组合继承 | 无问题 | 支持 | 1次(最优) |
注意事项
- 原型链继承会导致引用类型属性被所有实例共享
- 无法在创建子类实例时向父类构造函数传参
- 推荐使用寄生组合式继承作为最优方案
- 重写原型后必须修复
constructor
JavaScript
// 原型链继承适用场景
// 仅继承方法,父类没有引用类型属性
function Parent() {}
Parent.prototype.method = function() {};
function Child() {}
Child.prototype = new Parent(); // 可以使用
要点总结
- 原型链继承:子类原型指向父类实例
- 核心缺陷:引用类型属性被所有子类实例共享
- 无法在实例化时向父类传参
- 实际开发推荐寄生组合式继承(
Object.create+ 借用构造函数) - 重写原型后需修复
constructor - 判断继承关系:
instanceof和isPrototypeOf
📝 发现内容有误?点击此处直接编辑