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
阅读(353) | 评论(0) | 转发(0) |