call 和apply 方法能更好的体现javascript的函数式语言特性,在javaacript 中,几乎每一次编写函数式语言风格的代码都离不开call和apply,在javascript诸多版本的设计模式中,也用到了call和allpy
这是一个经常遇到的问题,我们先看下面的代码:
var obj = {
myName: 'sven',
getName: function(){
return this.myName;
}
};
console.log( obj.getName() ); //
输出:
'sven'
var getName2 = obj.getName;
console.log( getName2() ); //
输出:
undefined
当调用
obj.getName
时,
getName
方法是作为
obj
对象的属性被调用的,根据
2.1.1
节提到的规
律,此时的
this
指向
obj
对象,所以
obj.getName()
输出
'sven'
。
图灵社区会员 轩辕 专享 尊重版权
28
第
2
章
this
、
call
和
apply
当用另外一个变量
getName2
来引用
obj.getName
,并且调用
getName2
时,根据
2.1.2
节提到的
规律,此时是普通函数调用方式,
this
是指向全局
window
的,所以程序的执行结果是
undefined
。
再看另一个例子,document.getElementById这个方法名实在有些过长,我们大概尝试过用一些短的函数来代替它,如同prototype.js等一些框架所做过的事情,
var getId=function(id){
return document.getElementById(id)
}
getId('div1')
我们也许思考过为什么不能用下面这种更简单的方式
var getId = document.getElementById
getId('div1')
现在不妨花
1
分钟时间,让这段代码在浏览器中运行一次:
<html>
<body>
<div id="div1">
我是一个
div</div>
</body>
<script>
var getId = document.getElementById;
getId( 'div1' );
</script>
</html>
在Chrome,Firefox,IE10中执行过后就会发现,这段代码抛出了一个异常,这是因为许多引擎的document.getElementById 方法的内部实现中需要用到this。这个this本来被期望指向document,当getElementById方法作为document对象的属性被调用时,方法内部的this确实是指向document的。
但当用getId 来引用getElementById之后,再调用getId,此时就成了普通函数的调用,函数内部的this指向了window,而不是原来的document
我们可以尝试利用apply把document当作this传入getId函数,帮助修正this
document.getElementById = (function(func){
return function(){
return func.apply(document,arguments);
}
})(document.getElementById)
r getId = document.getElementById;
var div = getId( 'div1' );
alert (div.id); //
输出:
div1
call和apply
ECAMScript3给Function的原型定义了两个方法,他们是Function.prototype.call和Function.prototype.apply。在实际开发中,特别是在一些函数式风格的代码编写中,call,apply方法尤为有用,在javascript版本的设计模式中,这两个应用也非常广泛,能熟练运用这两个方法,市我们真正成为一名javascript程序员的重要一步
call apply 的区别
Function.prototype.call 和Function.prototype.apply 都是非常常用的方法,他们的作用一模一样,区别仅在于参数形式的不同。
apply接受两个参数,第一个参数指定了函数体内this对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组,apply方法把这个集合中的元素作为参数传递给被调用的函数
var func =function(a,b,c,){
alert([a,b,c]) //输出[1,2,3]
}
func.apply(null,[1,2,3]);
在这段代码中,参数1,2,3被放在数组中一起传入func函数,他们分别对应func参数列表中的a,b,c
call传入的参数的数量不固定,跟apply 相同的是,第一个参数也是代表函数体内的this指向,从第二个参数开始往后,每个参数被依次传入函数。
var func =function(a,b,c){
alert([a,b,c])//输出[a,b,c]
}
func.call(null,1,2,3)
当调用一个函数时,javascript的解释器并不会计较形参和实参的数量,类型以及顺序上的区别,javascript的参数在内部就是用一个数组来表示的。从这个意义上说,apply比call 的使用效率更高,我们不必关心有多少个参数被传入函数,只要用apply一股脑的推过去就行了。
call是包装在apply上面的一颗语法糖,如果我们明确的知道函数接受多少个参数,而且像一目了然的表达形参和实参的对应关系,那么也可以用call来传递参数
当使用call或者apply的时候,如果我们传入的第一个参数是null,函数体内的this会指向默认的宿主对象,在浏览器中则是window
var func = function( a, b, c ){
alert ( this === window ); //
输出
true
图灵社区会员 轩辕 专享 尊重版权
30
第
2
章
this
、
call
和
apply
};
func.apply( null, [ 1, 2, 3 ] );
但如果是在严格模式下,函数体内的
this
还是为
null
:
var func = function( a, b, c ){
"use strict";
alert ( this === null ); //
输出
true
}
func.apply( null, [ 1, 2, 3 ] );
有时候我们使用call或者apply的目的不在于指定this指向,而是另有用途,比如借用其他对象的方法,那么我们可以传入null来代替某个具体的对象
Math.max.apply(null,[1,2,3,4,5])//输出5
call和apply的用途
1、改变this的指向
call和apply最常见的用途是改变函数内部的this指向
var obj1={name:'obj1'};
var obj2={name:'obj2'};
window.name='window';
var getName=function(){alert(this.name)};
getName() //输出window
getName.call(obj1) //输出 obj1
getName.call(obj2)//输出obj2
当执行getName.call(obj1)这句代码时,getName 函数体内的this就指向obj1对象,所以此处执行的
var getName = function(){alert(this.name)}
实际上相当于:
var getName = function(){ alert(obj1.name) }