JavaScript 继承模式详解:借用构造函数与组合继承

作者:小括号

时间:2026-10-30

浏览:635

本文深入解析 JavaScript 中的两种重要继承模式。首先介绍借用构造函数技术,通过 call 或 apply 在子类中调用父类构造函数,解决原型包含引用类型值的问题并支持参数传递,但存在方法无法复用的缺陷。随后探讨组合继承,结合原型链与借用构造函数,既实现方法复用又保证实例属性独立,成为最常用的继承方式,同时也指出了其调用两次父构造函数的性能损耗。

文章目录

借用构造函数继承

借用构造函数继承

为了解决原型中包含引用类型值所带来的问题,开发人员引入了一种名为借用构造函数(constructor stealing,有时也称为伪造对象或经典继承)的技术。该技术的基本原理非常直观:在子类型的构造函数内部,直接调用超类型的构造函数。

基本模式


function SuperType(){
 this.colors = ["red", "blue", "green"];
}
function SubType(){
  //继承了SuperType
 SuperType.call(this);
}

var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green" 

基本思想

借用构造函数的核心在于利用 call 或 apply 方法,将父类中通过 this 绑定的属性和方法“借用”到子类创建的实例中。由于 this 对象是依据函数的执行环境在运行时绑定的——在全局环境中 this 指向 window,而作为对象方法调用时指向该对象——因此 call 和 apply 能够改变函数的执行上下文,将其从初始上下文切换为由 thisObj 指定的新对象。

因此,借用构造函数的执行逻辑是:当使用 new 操作符创建对象时(注意 new 与直接函数调用的区别,直接调用时 this 指向 window,而 new 创建时 this 指向新实例),会执行 SubType 内部的代码。SubType 内部通过 call 调用了 SuperType,此时 this 指向被重新绑定到新创建的实例上。这使得 SuperType 中定义的所有 this 相关属性和方法都被赋值给了这个新实例,而非 SuperType 本身。最终,所有实例都拥有了父类定义的这些通过 this 绑定的属性和方法。

优势

相较于原型链,借用构造函数具有一个显著优势:可以在子类型构造函数中向超类型构造函数传递参数。由于属性是绑定在 this 上的,它们在调用时才被赋值给相应的实例,因此各个实例的值互不干扰。

例如:


function SuperType(name){
this.name = name;
}
function SubType(){
//继承了SuperType,同时还传递了参数
SuperType.call(this, "Nicholas");
//实例属性
this.age = 29;
}
var instance = new SubType();
alert(instance.name); //"Nicholas";
alert(instance.age); //29 

劣势

如果仅使用借用构造函数,则无法避免构造函数模式的固有缺陷——所有方法都在构造函数内部定义,导致函数复用无从谈起。此外,超类型原型中定义的方法对子类型不可见,这意味着所有类型都只能依赖构造函数模式。鉴于这些局限性,借用构造函数很少被单独使用。

组合继承

组合继承

组合继承(combination inheritance),有时也被称为伪经典继承,是一种将原型链和借用构造函数技术结合起来的继承模式,旨在发挥两者的优势。

基本思想

其思路是利用原型链来继承原型上的属性和方法,同时通过借用构造函数来继承实例属性。这样既通过在原型上定义方法实现了函数复用,又确保了每个实例拥有自己独立的属性副本。

基本模型


function SuperType(name){
 this.name = name;
 this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
  alert(this.name);
};
function SubType(name, age){
//继承属性
 SuperType.call(this, name);
 this.age = age;
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27 

优势

组合继承避免了原型链和借用构造函数各自的缺陷,融合了它们的优点,已成为 JavaScript 中最常用的继承模式。

劣势

组合继承最大的问题在于,无论何种情况,都会调用两次超类型构造函数:一次是在创建子类型原型时,另一次是在子类型构造函数内部执行时。虽然子类型最终包含超类型对象的全部实例属性,但在调用子类型构造函数时,必须重写这些属性。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章