博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

call apply

Posted on 2012-08-31 15:51  bw_0927  阅读(277)  评论(0)    收藏  举报

http://blog.csdn.net/sunboy_2050/article/details/6592082

http://www.ijavascript.cn/jiaocheng/caller-callee-call-apply-464.html

 

http://www.cnblogs.com/jeffwongishandsome/archive/2008/10/15/1312113.html

看js的资料,随手记下一些认为实用的东东,以飨和我一样正在学习的诸位看官。
     “JavaScript 为函数对象定义了两个方法:apply 和call,它们的作用都是将函数绑定到另外一个对象上去运行,两者仅在定义参数的方式有所区别:
     Function.prototype.apply(thisArg,argArray); Function.prototype.call(thisArg[,arg1[,arg2…]]); 从函数原型可以看到,第一个参数都被取名为thisArg,也就是说,所有函数内部的this 指针都会被赋值为thisArg,这就达到了将函数作为另外一个对象的方法运行的目的。两个方法除了thisArg 参数,都是为Function 对象传递的参数。下面的代码说明了apply 和call 方法的工作方式:"(上面这段抄自<<征服ajax>>电子书,如有版权纠纷,请联系电子书作者,与本人无关)


function myFuncOne() {
    
this.p = "myFuncOne-";
    
this.A = function(arg) {
        alert(
this.p + arg);
    }
}

function myFuncTwo() {
    
this.p = "myFuncTwo-";
    
this.B = function(arg) {
        alert(
this.p + arg);
    }
}
function test() {
    
var obj1 = new myFuncOne();
    
var obj2 = new myFuncTwo();
    obj1.A(
"testA");                       //显示myFuncOne-testA 
    obj2.B("testB");                        //显示myFuncTwo-testB 
    obj1.A.apply(obj2, ["testA"]);          //显示myFuncTwo-testA,其中[ testA”]是仅有一个元素的数组
    obj2.B.apply(obj1, ["testB"]);          //显示myFuncOne-testB,其中[ testB”]是仅有一个元素的数组
    obj1.A.call(obj2, "testA");             //显示myFuncTwo-testA 
    obj2.B.call(obj1, "testB");             //显示myFuncOne-testB 
}


     代码是廉价的(Code is cheap),拷贝粘贴试一下,本人机器运行通过。
     函数对象还有一个与参数相关的属性length,这里看清楚了,是与“参数”相关的属性,没错,字面理解,就是参数个数,不说了,看代码:


function fun1(a, b, c) {

}
function fun2(a, b) {

}
function fun3() {

}
function test() {
    
for (var i = 1; i < 4; i++) {
        
var len = eval(("fun" + i) + ".length");
        alert(len);
    }

    
//    alert(fun1.length);
    //    alert(fun2.length);
    //    alert(fun3.length);
}


     与length属性不同,js有一个传递给函数的隐含参数,arguments,它也有一个length属性,下篇细说从头,洗洗睡了。

 

 

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

call和apply,它们的作用都是将函数绑定到另外一个对象上去运行

两者的格式和参数定义:

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

apply(thisArg [,argArray] ); // 参数数组,argArray

上面两个函数内部的this指针,都会被赋值为thisArg,这可实现将函数作为另外一个对象的方法运行的目的

一、call 的简单用法

首先,我们先看个简单的例子(call):

 

  1. <!doctype html>
  2. <html>
  3. <head>
  4. <title> call-apply </title>
  5. </head>
  6. <body>
  7. <input type="text" id="idTxt" value="input text">
  8. <script type="text/javascript">
  9. var value = "global var";
  10. function mFunc()
  11. {
  12. this.value = "member var";
  13. }
  14. function gFunc()
  15. {
  16. alert(this.value);
  17. }
  18. window.gFunc(); // show gFunc, global var
  19. gFunc.call(window); // show gFunc, global var
  20. gFunc.call(new mFunc()); // show mFunc, member var
  21. gFunc.call(document.getElementById('idTxt')); // show element, input text
  22. </script>
  23. <script language="javascript">
  24. var func = new function()
  25. {
  26. this.a = "func";
  27. }
  28. var func2 = function(x)
  29. {
  30. var a = "func2";
  31. alert(this.a);
  32. alert(x);
  33. }
  34. func2.call(func, "func2"); // show func and func2
  35. </script>
  36. </body>
  37. </html>
<!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 继承用法与改进

js使用call模拟继承

测试代码:

 

  1. <!doctype html>
  2. <html>
  3. <head>
  4. <title> call - apply for inherit </title>
  5. </head>
  6. <body>
  7. <script type="text/javascript">
  8. function baseA() // base Class A
  9. {
  10. this.member = "baseA member";
  11. this.showSelfA = function()
  12. {
  13. window.alert(this.member);
  14. }
  15. }
  16. function baseB() // base Class B
  17. {
  18. this.member = "baseB member";
  19. this.showSelfB = function()
  20. {
  21. window.alert(this.member);
  22. }
  23. }
  24. function extendAB() // Inherit Class from A and B
  25. {
  26. baseA.call(this); // call for A
  27. baseB.call(this); // call for B
  28. }
  29. window.onload = function()
  30. {
  31. var extend = new extendAB();
  32. extend.showSelfA(); // show A
  33. extend.showSelfB(); // show B
  34. }
  35. </script>
  36. </body>
  37. </html>
<!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浏览器的调试工具,下断点,进行调试,结果发现:

当调用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调试信息也验证了我们的正确性:

继承改进(prototype)

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

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

改进举例如下:

 

  1. <!doctype html>
  2. <html>
  3. <head>
  4. <title> call - apply for prototype </title>
  5. </head>
  6. <body>
  7. <script type="text/javascript">
  8. var Class = {
  9. create: function() // create Function
  10. {
  11. return function()
  12. {
  13. this.initialize.apply(this, arguments);
  14. }
  15. }
  16. };
  17. var Person = Class.create(); // Create Class Person
  18. Person.prototype = { // prototype initialize
  19. initialize: function(obj1, obj2)
  20. {
  21. this.obj1 = obj1;
  22. this.obj2 = obj2;
  23. },
  24. showSelf: function()
  25. {
  26. alert("obj: " + this.obj1 + " and " + this.obj2);
  27. }
  28. }
  29. // instance Class
  30. var person = new Person("man", "women"); // two params
  31. person.showSelf(); // show person
  32. </script>
  33. </body>
  34. </html>
<!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