Chinaunix首页 | 论坛 | 博客
  • 博客访问: 175576
  • 博文数量: 89
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 828
  • 用 户 组: 普通用户
  • 注册时间: 2013-10-08 10:44
文章分类
文章存档

2014年(9)

2013年(80)

我的朋友

分类: Java

2013-11-17 16:22:12

JavaScript远比Java等语言更灵活,call和apply就一个体现。


区分apply,call就一句话,


foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments)==this.foo(arg1, arg2, arg3)
call, apply都属于Function.prototype的一个方法,它是JavaScript引擎内在实现的,因为属于Function.prototype,所以每个Function对象实例,也就是每个方法都有call, apply属性.既然作为方法的属性,那它们的使用就当然是针对方法的了.这两个方法是容易混淆的,因为它们的作用一样,只是使用方式不同.


相同点:两个方法产生的作用是完全一样的


不同点:方法传递的参数不同


那什么是方法产生的作用,方法传递的参数是什么呢?


我们就上面的foo.call(this, arg1, arg2, arg3)展开分析.


foo是一个方法,this是方法执行时上下文相关对象,arg1, arg2, arg3是传给foo方法的参数.这里所谓的方法执行时上下文相关对象, 如果有面向对象的编程基础,那很好理解,就是在类实例化后对象中的this.


在JavaScript中,代码总是有一个上下文对象,代码处理该对象之内. 上下文对象是通过this变量来体现的, 这个this变量永远指向当前代码所处的对象中.


因为apply与call产生的作用是一样的,可以说


call, apply作用就是借用别人的方法来调用,就像调用自己的一样.


好,理解了call, apply相同处—–作用后,再来看看它们的区别,看过上面例子,相信您大概知道了.


从 b.setMessage.call(a, “a的消息”) 等效于 a.setMessage( “a的消息”) 可以看出, “a的消息”在call中作为一个参数传递,


那么在apply中是怎么表示的呢,直接解释说不清楚,apply要结合应用场景才一目了然.我们来设计一个应用场景:


[javascript] view plaincopy
function print(a, b, c, d) {  
  alert(a + b + c + d);  
}  
  
function example(a, b, c, d) {  
  //用call方式借用print,参数显式打散传递  
  print.call(this, a, b, c, d);  
  //用apply方式借用print, 参数作为一个数组传递,  
  //这里直接用JavaScript方法内本身有的arguments数组  
  print.apply(this, arguments);  
  //或者封装成数组  
  print.apply(this, [a, b, c, d]);  
}  
//下面将显示”背光脚本”  
example("Hello", "JavaScript", "Programming", "Language");  


在这场景中, example方法内,a, b, c, d作为方法传递的参数, 方法分别运用了apply, call去借print方法来调用,


最后一句由于直接调用example方法, 所以在该方法中的上下文对象this就是window对象.


所以,call, apply方法它们除了第一个参数,即执行时上下文对象相同外,call方法的其它参数将依次传递给借用的方法作参数,而apply就两个参数,第二个参数为一个数组传递.所以可以说成


call, apply方法区别是,从第二个参数起, call方法参数将依次传递给借用的方法作参数, 而apply直接将这些参数放到一个数组中再传递, 最后借用方法的参数列表是一样的.


 


应用场景:


当参数明确时可用call, 当参数不明确时可用apply给合arguments


[javascript] view plaincopy
//例  
print.call(window, "Hello", "JavaScript", "Programming", "Language");  
//foo参数可能为多个  
function foo(){  
print.apply(window, arguments);   
}  




================================================================================================
call 方法 
请参阅 
应用于:Function 对象 
要求 
版本 5.5 
调用一个对象的一个方法,以另一个对象替换当前对象。 
call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 
参数 
thisObj 
可选项。将被用作当前对象的对象。 
arg1, arg2, , argN 
可选项。将被传递方法参数序列。 
说明 
call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。 
如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。 
------------------------------------------------------------------------------------------- 
乍一看,很容易把人看迷胡,先做一些简单的说明 
obj1.method1.call(obj2,argument1,argument2) 
如上,call的作用就是把obj1的方法放到obj2上使用,后面的argument1..这些做为参数传入. 
举一个具体的例子 
[javascript] view plaincopy
function add(a, b) {  
  alert(a + b);  
}  
  
function sub(a, b) {  
  alert(a - b);  
}  
add.call(sub, 3, 1);  


这个例子中的意思就是用 add 来替换 sub,add.call(sub,3,1) == add(3,1) ,所以运行结果为:alert(4); // 注意:js 中的函数其实是对象,函数名是对 Function 对象的引用。 


看一个稍微复杂一点的例子 
[javascript] view plaincopy
function Class1() {  
  this.name = "class1";  
  this.showNam = function() {  
    alert(this.name);  
  }  
}  
  
function Class2() {  
  this.name = "class2";  
}  
var c1 = new Class1();  
var c2 = new Class2();  
c1.showNam.call(c2);  


注意,call 的意思是把 c1 的方法放到c2上执行,原来c2是没有showNam() 方法,现在是把c1 的showNam()方法放到 c2 上来执行,所以this.name 应该是 class2,执行的结果就是 :alert("class2"); 
怎么样,觉得有意思了吧,可以让a对象来执行b对象的方法,这是java程序员所不敢想的。还有更有趣的,可以用 call 来实现继承 
[javascript] view plaincopy
function Class1() {  
  this.showTxt = function(txt) {  
    alert(txt);  
  }  
}  
  
function Class2() {  
  Class1.call(this);  
}  
var c2 = new Class2();  
c2.showTxt("cc");  


这样 Class2 就继承Class1了,Class1.call(this) 的 意思就是使用 Class1 对象代替this对象,那么 Class2 中不就有Class1 的所有属性和方法了吗,c2 对象就能够直接调用Class1 的方法以及属性了,执行结果就是:alert(“cc”);
对的,就是这样,这就是 javaScript 如何来模拟面向对象中的继承的,还可以实现多重继承。
[javascript] view plaincopy
function Class10()   
{   
this.showSub = function(a,b)   
{   
alert(a-b);   
}   
}   
function Class11()   
{   
this.showAdd = function(a,b)   
{   
alert(a+b);   
}   
}   
  
function Class2()   
{   
Class10.call(this);   
Class11.call(this);   
}   


很简单,使用两个 call 就实现多重继承了 
当然,js的继承还有其他方法,例如使用原型链,这个不属于本文的范畴,只是在此说明call 的用法 
说了call ,当然还有 apply,这两个方法基本上是一个意思 
区别在于 call 的第二个参数可以是任意类型,而apply的第二个参数必须是数组,也可以是arguments 
还有 callee,caller,这个和call的 用法就不同了,放到下次讲吧 ,呵呵。


========================================================================================================


Javascript中call的使用  


Javascript中call的使用自己感觉蛮纠结的,根据文档很好理解,其实很难确定你是否真正的理解。
call 方法
应用于:Function 对象
调用一个对象的一个方法,以另一个对象替换当前对象。
call([thisObj[,arg1[, arg2[,   [,.argN]]]]])
参数:
thisObj 
可选项。将被用作当前对象的对象。 
arg1, arg2, , argN 
可选项。将被传递方法参数序列。 
说明:
call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
1.最基本的理解:示例1
自定义一个类,该类有一个方法showTxt,用来显示当前对象的name值。
创建一个对象,并且该对象的name值等于test1。
使用call方法,使新创建的对象obj添加Class1类的showTxt方法,即把Class1类中的this.showTxt中的this指定成obj,这样obj就有了showTxt方法。弹出"test1"。
[javascript] view plaincopy
function Class1() {  
  this.showTxt = function() {  
    alert(this.name)  
  }  
}  
var obj = new Object();  
obj.name = "test1"  
Class1.call(obj);  
obj.showTxt(); //test1  
alert(obj.showTxt); //function(){alert(this.name)}  


这个例子很容易理解。
2.再看一个稍微深入的理解
示例2:创建Class1的实例,让这个实例调用showTxt方法返回这个实例的name值,因为这个实现没有name值所以返回undefine.


[javascript] view plaincopy
function Class1() {  
  this.showTxt = function() {  
    alert(this.name)  
  }  
}  
var class1 = new Class1();  
class1.showTxt(); //undefined  
alert(class1.showTxt); //function(){alert(this.name)}  


示例3:下面就给Class1添加个name值,这时class1再调用showTxt方法,会返回class1,这是因为给类添加了name值,所以实例的name也由undefine变成了class1.
[javascript] view plaincopy
function Class1() {  
  this.name = 'class1'; //添加name值  
  this.showTxt = function() {  
    alert(this.name)  
  }  
}  
var class1 = new Class1();  
class1.showTxt(); //class1  
alert(class1.showTxt); //function(){alert(this.name)}  


示例4:Class1.call(obj) 这个操作把Class1中的this.name,this.showTxt里的this替换成了obj,所以就变成了obj.name='class1',所以obj.showTxt在执行时返回class1。
[javascript] view plaincopy
function Class1() {  
  this.name = 'class1'; //添加name值  
  this.showTxt = function() {  
    alert(this.name)  
  }  
}  
  
function Class2() {  
  this.name = 'class2';  
}  
var class2 = new Class2();  
Class1.call(class2);  
alert(class2.showTxt); //function(){alert(this.name)}  
class2.showTxt(); //class1  


示例5:如果在Class1.call(obj);之后再添加obj.name = 'test1',最后结果会输入test1,原因显而易见。
[javascript] view plaincopy
function Class1() {  
  this.name = 'class1'; //添加name值  
  this.showTxt = function() {  
    alert(this.name)  
  }  
}  
  
function Class2() {  
  this.name = 'class2';  
}  
var class2 = new Class2();  
Class1.call(class2);  
class2.name = 'test1'; //重定义obj.name值  
alert(class2.showTxt); //function(){alert(this.name)}  
class2.showTxt(); //test1  


上面的例子call的都是一个对象的实例,接下来的案例把对象的实例直接换成函数,看看执行结果会发生哪些变化
3.把call方法的第一参数由实例换成函数看看会怎么
示例6:Class2是一个function对象的引用,在执行Class1.call(Class2)时this.showTxt里的this被替换成了Class2。这样Class2就有了showTxt方法,Class2.showTxt()执行时会返回Class2.name的值
[javascript] view plaincopy
function Class1() {  
  this.showTxt = function() {  
    alert(this.name)  
  }  
}  
  
function Class2() {  
  this.name = 'class2';  
}  
Class1.call(Class2);  
alert(Class2.showTxt); //function(){alert(this.name)}  
Class2.showTxt(); //Class2  






4.接着看下面的例子
示例7:class1.showTxt.call(class2);之所以会返回class2是因为function(){alert(this.name)}这里的this被call指定成了class2,变成了alert(class2.name),所以会返回class2.
alert(class2.showTxt)返回undefined,说明并未定义class2.showTxt方法。
[javascript] view plaincopy
function Class1() {  
  this.showTxt = function() {  
    alert(this.name)  
  }  
}  
  
function Class2() {  
  this.name = 'class2';  
}  
var class1 = new Class1();  
var class2 = new Class2();  
class1.showTxt.call(class2); //class2  
alert(class2.showTxt); //undefined  


因为并为给class2添加showTxt方法,所以提示该错误。如果在这个调用之前添加Class1.call(class2);这个调用就OK了
[javascript] view plaincopy
Class1.call(class2);  
class2.showTxt();//class1  


[javascript] view plaincopy
function Class1(){  
    this.showTxt = function(){alert(this.name)}  
}  
function Class2(){  
    this.name = 'class2';  
}  
var class1 = new Class1();  
class1.showTxt.call(Class2);//Class2  
alert(Class2.showTxt);//undefined  


5.在使用call时如果调用函数里没有this会怎么样
示例9:


[javascript] view plaincopy
function add(a, b) {  
  alert(a + b);  
}  
  
function sub(a, b) {  
  alert(a - b);  
}  
add.call(sub, 3, 1); //4  


结果返回4,add.call(sub,3,1)在执行过程中,sub做为add函数中this的替代品出现,但是因为add里没有用到this,所以sub函数直接忽略,所以结果是4。
所以实际执行如下:返回4。


[javascript] view plaincopy
function add(a,b){  
    alert(a+b);  
}  
add(3,1);//4  
阅读(322) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~