JavaScript中call与apply的用法详解及继承模拟

作者:代码拾荒时间:2026-10-28 06:59:46浏览:415

本文深入解析JavaScript中call与apply的核心用法,阐明两者均用于绑定函数执行时的this对象。通过对比参数格式差异,结合具体代码示例演示this指向变化。重点分析使用call模拟继承时成员变量被覆盖的问题,并给出基于prototype原型的改进方案,帮助开发者理解函数绑定与对象继承机制。

call与apply的基本概念与语法

前天去面试,有个gg问了一些js知识,其中有一道call与apply用法的题目,尽管在365天前用过call方法,但当时还是没能答上来,今天深入总结一下

call和apply的主要功能是将函数绑定到另一个对象上执行。

两者的语法格式与参数定义如下:

call( thisArg [,arg1,arg2,… ] ); // 参数列表,arg1,arg2,... apply(thisArg [,argArray] ); // 参数数组,argArray

在上述两个函数内部,this指针将被赋值为thisArg,从而实现将函数作为另一个对象的方法执行的目的。

call方法的简单用法与this指向分析

一、call 的简单用法

首先,通过一个简单的示例来展示call的使用:

代码如下:

<!doctype html>
<html>
 <head>
 <title> call-apply </title>
 </head>
<body>
 <input type="text" id="idTxt" value="input text">
 <script type="text/javascript">
 var value = "global var";
 function mFunc()
 {
 this.value = "member var";
 }
 function gFunc()
 {
 alert(this.value);
 } 
 window.gFunc(); // show gFunc, global var
 gFunc.call(window); // show gFunc, global var
 gFunc.call(new mFunc()); // show mFunc, member var
 gFunc.call(document.getElementById('idTxt')); // show element, input text
 </script>
 <script language="javascript">
 var func = new function()
 {
 this.a = "func";
 }
 var func2 = function(x)
 {
 var a = "func2";
 alert(this.a); 
 alert(x);
 }
 func2.call(func, "func2"); // show func and func2
 </script>
 </body>
</html>

运行结果如下:

global var
global var
member var
input text
func
func2

测试环境:Google Chrome 10.0.648.45

最后,对结果进行分析

1、全局对象window调用函数gFunc,this指向window对象,因此this.value为global var

2、函数gFunc调用call方法,this默认指向第一个参数window对象,因此this.value也为global var

3、函数gFunc调用call方法,this默认指向第一个参数new mFunc(),即mFunc的对象,因此this.value为mFunc的成员变量member var

4、函数gFunc调用call方法,this默认指向第一个参数input text控件,即id=‘idTxt'的控件,因此this.value为input控件的value值input text

5、函数func2调用call方法,this默认指向第一个参数func函数对象,因此this.value为this.a,即func

6、函数func2调用call方法,第二个参数属于函数对象func2的参数,因此alert(x)为第二个参数func2


使用call模拟继承及问题排查

二、call 继承用法与改进

js使用call模拟继承

测试代码:

代码如下:

<!doctype html>
<html>
 <head>
 <title> call - apply for inherit </title>
 </head>
 <body>
 <script type="text/javascript">
 function baseA() // base Class A
 {
 this.member = "baseA member";
 this.showSelfA = function()
 {
 window.alert(this.member);
 }
 }
 function baseB() // base Class B
 {
 this.member = "baseB member";
 this.showSelfB = function()
 {
 window.alert(this.member);
 }
 }
 function extendAB() // Inherit Class from A and B
 {
 baseA.call(this); // call for A
 baseB.call(this); // call for B
 }
 window.onload = function()
 {
 var extend = new extendAB(); 
 extend.showSelfA(); // show A
 extend.showSelfB(); // show B
 }
 </script>
 </body>
</html>
运行结果如下:

baseB member baseB member

测试环境:Google Chrome 10.0.648.45

结果分析:

预期的结果,应该是输出 baseA member 和 baseB member,但实际输出却是 baseB member 和 baseB member

(已在IE9、8、6,Maxthon、Chrome、FF、Opera、Safari、360等浏览器测试过,结果都是后者:baseB member)

至此,机器是不会错的,这就需要我们深入分析

我们可能会很容易想到是this引起的,this两次都指向了baseB对象,但是推测真是这样吗?

为了探究实质,我们借助chrome浏览器的调试工具,下断点,进行调试,结果发现:

call与apply的基本概念与语法

当调用extend.showSelfA();时,此时的this指向extendAB(并不是我们推测的两次都指向baseB对象)

真实原因是extendAB对象的成员变量member在被baseB.call(this);实例化时,被baseB的成员member覆盖了,即extendAB的成员member由baseA member赋值成了baseB member

当然,我们也可以对上面baseA代码稍作修改,来验证我们调试分析的正确性:

代码如下:

function baseA() // base Class A
{
this.memberA = "baseA member"; // member改成memberA,以区分baseB中的member
this.showSelfA = function()
 {
window.alert(this.memberA); // 显示memberA
 }
}

再次运行chrome等浏览器,结果如下:

baseA member baseB member

结果和我们的预期相同,同时chrome调试信息也验证了我们的正确性:

call与apply的基本概念与语法

基于prototype的继承改进方案

继承改进(prototype)

以上模拟继承方法,仔细分析不是最好的。

因为每次在函数(类)中定义了成员方法,都会导致实例有副本,因此可以借助prototype原型,进行改进

改进举例如下:

代码如下:

<!doctype html>
<html>
 <head>
 <title> call - apply for prototype </title>
 </head>
 <body>
 <script type="text/javascript">
 var Class = {
 create: function() // create Function
 {
 return function()
 {
 this.initialize.apply(this, arguments);
 }
 }
 };
 var Person = Class.create(); // Create Class Person
 Person.prototype = { // prototype initialize
 initialize: function(obj1, obj2)
 {
 this.obj1 = obj1;
 this.obj2 = obj2;
 },
 showSelf: function()
 {
 alert("obj: " + this.obj1 + " and " + this.obj2);
 }
 }
 // instance Class
 var person = new Person("man", "women"); // two params
 person.showSelf(); // show person
 </script>
 </body>
</html>

运行结果如下:


obj: man and women
作者最新文章