JavaScript实现继承:详解call()与apply()方法
在JavaScript传统面向对象的开发模式中,除了常见的原型链继承,利用 call() 和 apply() 方法进行"对象冒充"是实现继承的重要方式之一。本文将详细解析这两种方法的执行原理、语法差异及其在继承中的应用场景,并说明其主要限制。
核心目标与准备工作
call() 和 apply() 的核心作用在于改变函数执行时的上下文环境(即 this 关键字的指向)。在继承场景中,我们的目标是将父类的构造函数绑定到子类实例上,从而让子类拥有父类的属性与方法。
- 适用场景:需要为实例化对象添加属于父类的属性和私有方法。
- 前置条件:已定义好父类构造函数和子类构造函数。
- 关键认知:这种方法实现的继承仅针对构造函数内部的属性,不涉及原型链上的共享方法。
call() 方法实现继承
call() 方法与经典的对象冒充(Object Masquerading)最为相似。它允许指定一个对象作为 this 的目标,并将剩余参数逐一传递给被调用的函数。
第1步:基础调用演示
目的:理解如何将外部函数的 this 指向指定对象。
操作:
1. 定义一个引用 this.name 的全局函数;
2. 创建一个目标对象并赋值;
3. 使用 call() 将函数与该对象绑定并执行。
function sayHello(sPrefix, sSuffix) {
alert(this.name + " says " + sPrefix + sSuffix);
}
var obj = new Object();
obj.name = "Tom";
// 第一个参数obj决定了函数内部this的指向
// 后续参数依次对应sayHello的形参
sayHello.call(obj, "Hello ", "World.");
完成状态:控制台弹出包含 "Tom says Hello World." 的消息框,证明 sPrefix 和 sSuffix 已成功接收传值。
第2步:在继承中应用 call()
目的:子类构造函数通过 call() 调用父类构造函数,以继承父类属性。
操作:
1. 定义父类 ClassA,初始化颜色属性;
2. 定义子类 ClassB,在其构造函数内部替换原有的对象冒充代码;
3. 调用 ClassA.call(this, sColor)。
// 父类构造函数
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function () {
alert(this.color);
};
}
// 子类构造函数
function ClassB(sColor, sName) {
// 使用 call 将 ClassA 中的 this 指向当前新建的 ClassB 实例
ClassA.call(this, sColor);
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}
关键参数:this(当前子类实例)、sColor(从子类传给父类的参数)。
完成状态:ClassB 的实例拥有了 color 属性和 sayColor 方法,以及自身的 name 属性。
apply() 方法实现继承
apply() 方法与 call() 类似,区别在于参数的传递方式。apply() 接收两个参数:用作 this 的对象和一个参数数组(或类数组对象)。
第1步:基础调用演示
目的:通过数组形式传递参数。
操作:
function sayColor(sPrefix, sSuffix) {
alert(sPrefix + this.color + sSuffix);
}
var obj = new Object();
obj.color = "blue";
// 第二个参数必须是数组
sayColor.apply(obj, new Array("The color is ", "a very nice color indeed."));
完成状态:生成完整的提示信息,证明数组元素按顺序映射到了函数的形参中。
第2步:在继承中应用 apply()
目的:展示两种不同的数组传递策略——显式构造数组与直接使用 arguments 对象。
操作 A:使用 new Array()
function ClassB(sColor, sName) {
// 手动构建包含所需参数的数组
ClassA.apply(this, new Array(sColor));
this.name = sName;
// ...其他属性...
}
操作 B:使用 arguments 对象(高阶技巧)
function ClassB(sColor, sName) {
// 直接将当前函数的 arguments 对象传递给父类
// 注意:这要求父子类参数顺序完全一致
ClassA.apply(this, arguments);
this.name = sName;
// ...其他属性...
}
风险提示:直接使用 arguments 的前提是父类和子类的参数列表顺序必须严格匹配。如果子类有额外参数而父类没有,或者顺序不同,则需手动创建数组并按正确顺序排列参数,此时应回到使用显式数组的方式或使用 call()。
局限性与总结
call() 和 apply() 使得继承的代码编写比传统的对象冒充更加简洁直观。然而,这两种方法存在一个明显的弊端:**它们只能继承构造函数内定义的属性,无法继承父类原型链(Prototype)上声明的方法或属性**。
如果需要解决原型继承的问题,通常需要结合原型链继承或其他模式(如组合继承)来共同实现完善的面向对象继承结构。
以上就是基于 JavaScript 实现继承机制之调用 call() 与 apply() 的详细内容,更多关于 JavaScript 面向对象编程的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









