组合继承的核心在于两步:在子类构造函数中通过call或apply调用父类构造函数以继承属性;将父类的实例通过object.create(parent.prototype)赋值给子类原型以继承方法,并修正constructor指向。2. 这种方式既保证了实例属性的独立性,又实现了方法的共享,解决了原型链继承中引用类型属性共享的问题和构造函数继承中方法无法复用的缺点。3. 尽管存在父类构造函数可能被调用两次的潜在问题(若使用new parent()设置原型),但通过object.create可避免,且es6的class语法糖通过extends和super()自动完成了组合继承的所有步骤,包括属性继承、原型链连接和constructor修正,使继承实现更简洁、安全、可读。4. 因此,组合继承因其平衡性与实用性成为es6前最广泛使用的继承模式,而class语法则是其现代化、优雅的封装,推荐在现代javascript开发中优先使用。

JavaScript中实现原型链的组合继承,核心在于两步:在子类的构造函数中调用父类的构造函数来继承属性,以及将父类的实例赋值给子类的原型以继承方法。这样既能保证每个实例有独立的属性,又能让所有实例共享父类的方法。

组合继承,顾名思义,就是将构造函数继承和原型链继承两者结合起来。我个人觉得,这是JavaScript早期最实用、也最被广泛接受的继承模式,因为它很好地平衡了属性的独立性和方法的共享性。
具体来说,我们通常这样做:

属性继承: 在子类的构造函数内部,通过
call
apply
this.name
方法继承: 将父类的一个新实例赋值给子类的原型对象。这样,子类的所有实例都能通过原型链访问到父类原型上定义的方法。这里通常会用到
Object.create()
同时,我们还需要修正子类原型的
constructor
// 父类
function Parent(name) {
this.name = name;
this.hobbies = ['reading', 'coding']; // 引用类型属性
}
Parent.prototype.sayName = function() {
console.log('My name is ' + this.name);
};
Parent.prototype.addHobby = function(hobby) {
this.hobbies.push(hobby);
};
// 子类
function Child(name, age) {
// 1. 属性继承:调用父类构造函数,继承父类的实例属性
Parent.call(this, name); // 确保子类实例拥有独立的name和hobbies属性
this.age = age;
}
// 2. 方法继承:将父类原型上的方法继承给子类
// 使用 Object.create() 创建一个新对象,其原型指向 Parent.prototype
Child.prototype = Object.create(Parent.prototype);
// 修正 constructor 属性,使其指向 Child 构造函数
Child.prototype.constructor = Child;
Child.prototype.sayAge = function() {
console.log('I am ' + this.age + ' years old.');
};
// 实际使用
const child1 = new Child('Alice', 10);
child1.sayName(); // My name is Alice
child1.sayAge(); // I am 10 years old.
child1.addHobby('drawing');
console.log(child1.hobbies); // ["reading", "coding", "drawing"]
const child2 = new Child('Bob', 8);
child2.sayName(); // My name is Bob
child2.sayAge(); // I am 8 years old.
console.log(child2.hobbies); // ["reading", "coding"] - 验证了引用类型属性的独立性
// 验证原型链
console.log(child1 instanceof Child); // true
console.log(child1 instanceof Parent); // true这段代码展示了组合继承的完整流程。它巧妙地解决了JavaScript继承中的两个核心痛点:确保实例属性的独立性,以及实现方法的高效共享。
说实话,在ES6的
class
首先,它完美地解决了“实例属性独立性”和“共享方法效率”之间的矛盾。在JavaScript中,如果你只用原型链继承,那么父类原型上的引用类型属性(比如数组或对象)会被所有子类实例共享,一个实例修改了,其他实例也会受影响,这显然不是我们想要的。而如果只用构造函数继承,虽然属性独立了,但每个实例都会创建一套自己的方法副本,这在内存上是巨大的浪费。组合继承则巧妙地规避了这两个问题,属性归实例独有,方法则通过原型链共享,效率和独立性兼得。
其次,它保持了
instanceof
child1 instanceof Child
true
child1 instanceof Parent
true
再者,它的实现方式相对直观。虽然看起来有两步(调用构造函数和设置原型),但每一步的目的都非常明确,容易理解和调试。它不像寄生组合式继承那样,需要更深入地理解中间对象的创建。
所以,在我看来,组合继承的流行并非偶然,而是其在实际开发中展现出的强大实用性和鲁棒性,让它在很长一段时间内都是JavaScript开发者实现继承的首选。
尽管组合继承被广泛使用,但它也并非完美无缺,存在一些值得我们注意的“小瑕疵”:
最大的一个“缺点”,或者说需要注意的地方,就是父类构造函数会被调用两次。你看,第一次是在子类构造函数内部通过
Parent.call(this, name)
Child.prototype = new Parent()
Object.create(Parent.prototype)
new Parent()
Object.create(Parent.prototype)
Object.create
Child.prototype = new Parent()
如果父类构造函数内部有比较耗时的操作,或者会产生副作用(比如打印日志、初始化复杂资源),那么被调用两次就可能带来不必要的开销或者重复操作。不过,说实话,对于大多数轻量级的构造函数来说,这种性能影响通常可以忽略不计。这更多的是一种设计上的“不优雅”,而不是实际的性能瓶颈。
另外,就是我前面提到的constructor
constructor
instance.constructor
这些问题,在我看来,更多是JavaScript原型继承机制本身的特性所带来的,而不是组合继承模式独有的严重缺陷。理解这些,能帮助我们更好地使用和调试代码。
ES6引入的
class
class
当你使用
extends
super()
class
调用父类构造函数: 在子类的
constructor
super()
constructor
super()
super()
this
设置原型链:
extends
修正 constructor
class
constructor
我们来看一个ES6
class
// 父类 (ES6 Class)
class ParentClass {
constructor(name) {
this.name = name;
this.hobbies = ['reading', 'coding'];
}
sayName() {
console.log('My name is ' + this.name);
}
addHobby(hobby) {
this.hobbies.push(hobby);
}
}
// 子类 (ES6 Class)
class ChildClass extends ParentClass {
constructor(name, age) {
super(name); // 相当于 ParentClass.call(this, name)
this.age = age;
}
sayAge() {
console.log('I am ' + this.age + ' years old.');
}
}
// 实际使用
const es6Child1 = new ChildClass('Charlie', 12);
es6Child1.sayName(); // My name is Charlie
es6Child1.sayAge(); // I am 12 years old.
es6Child1.addHobby('singing');
console.log(es6Child1.hobbies); // ["reading", "coding", "singing"]
const es6Child2 = new ChildClass('David', 9);
console.log(es6Child2.hobbies); // ["reading", "coding"] - 独立性依然保持
console.log(es6Child1 instanceof ChildClass); // true
console.log(es6Child1 instanceof ParentClass); // true你看,使用
class
Parent.call()
Object.create()
constructor
extends
super()
以上就是js怎么实现原型链的组合继承的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号