JavaScript中的call()和apply()
函数的角色:普通函数,对象,构造器`
先举一个例子
<script>
function f1(){
console.log(this);
}
f1();
</script>
现在要问大家,调用了函数 f1 之后的输出结果是什么?
答案是:window
顺带提一下JavaScript执行模式,对你的理解可能会加深一步。
JavaScript执行模式有两种:正常模式,严格模式。
我们平常使用都是正常模式,但在严格模式下,我们执行上面的代码就会出现问题!
严格模式的使用:
<script>
"use strict";//进入严格模式
function f1(){
console.log(this);
}
f1();
</script>
大家会发现输出的结果是undefined
,此时我们改一下代码,把 f1函数 的调用改成window.f1();就会发现正常输出了结果,打印出了window对象。
进入重点
先大概理解一下这三点:
- call和apply作用其实是一样的。
- call和apply的不同在于传值的不同。
- call把参数作为离散的值来传,apply则必须把参数放在一个数组里来传。
接下来我们通过call和apply来调用f1函数。【注意:这里使用call和apply时,不是在严格模式下,严格模式下使用,输出的undefined】
<script>
function f1(){
console.log(this);
}
f1.call();
f1.apply();
// 两个输出的都是window对象
</script>
上面的f1函数是无参的,那如果有参数呢?【这点是有变化的,需要注意。】
通过call和apply来调用有参数的f1函数?
用例子说话 (为了区别改一下函数名字,改为f2
函数):
<script>
function f2(a,b){
console.log((a + b) + " : " + this);
}
f2(1,2);
f2.call(1,2);
f2.call(null,1,2);
f2.apply(null,[1,2]);
f2.apply(obj,[1,2]);
</script>
输出结果:
3 : [object Window]
NaN : 1
3 : [object Window]
3 : [object Window]
ReferenceError: obj is not defined
到现在,如果你运行了上面的代码,你可能会有疑问,不过不用急,先看下面的问题。
问题1:
f2.call(null,1,2);这里面的null起了什么作用。
答案1:
null
值,代表的就是指向当前的函数调用者,f2函数的调用者就是window(在console的输出中有显示),函数被window调用,显然合情合理。
问题2:
为什么在apply中传入了obj,却显示obj is not defined?
答案2:
由 答案1 我们可以推出来一点,第一参数传过去之后肯定指向了函数的调用者,那么问题来了,我们传过去的obj是调用者吗?我们甚至没有定义它。
于此同理,我们看到,第二个f2.call(1,2)
的输出显示的函数调用者为 1 ,【输出的this是1】
看了这个例子,想必你已经理解了重点开头的三条的含义。
总结一下:call和apply都是Function对象的原型上定义的方法,它的作用是调用当前函数,并且可以通过call和apply来改变当前函数中的this的指向。
call的签名:call ( thisArg, arg1, arg2…);
apply的签名:apply ( thisArg, [item1, item2, item3…] );
- 当函数中没有形参,用这二者调用时也可一个参数都不给,包括thisArg,默认this指向window。
- 如果必须传参数,当通过call和apply调用时,参数必须在对象的后边,且第一个参数必须是对象,至少要给null(指向window)
注:通过thisArg传另一个对象,来改变this关键字的当前指向。(以前this指向调用者,现在指向指定的对象)。
如果你还是感觉模糊的话,看完下面的这个例子,想必你就会彻底明白。
输出结果:
per.sayHi(); --> 你好:东哥,28
per.sayHi.call(null); --> 你好:,undefined
per.sayHi.call(per); --> 你好:东哥,28
per.sayHi.call(stu); --> 你好:黎明,18
per.sayHi.apply(stu); --> 你好:黎明,18
我只说一点,想必大家就明白了,在这个例子中,我们改变了this的指向,原本指向Person,而per.sayHi.call(stu);则改变了这一指向。所以Student才可调用sayHi()方法
说了这么多,call和apply有什么用呢?
call和apply的作用有二:一为方法借用,二用在JavaScript继承
在这里我们只讲第一个方法借用
案例1:求一个数组的最大值
<script>
var max = Math.max(1,2,33,55,11);
console.log(max);
<script>
这是一个简单的求最大值的函数,但是如果我们传入的不是数,而是数组呢?
你肯定会脱口而出:不能传入数组,会报错。
那有没有方法可以传入呢?答案是有,用我们的apply。
<script>
//修改代码
var ary = [1,3,44,5,77,6];
var m = Math.max.apply(null,ary);
console.log(m);
<script>
在求最大值时,max只接受离散的值,不接受数组,如果我们要传数组,就可以使用apply()传入数组对象。
案例2:把伪数组转换为普通数组
<script>
var wsz = {0:'james',1:'zs',2:'zsf',length:3};//伪数组
var realAry = [ ].slice.call(wsz);
console.log(realAry);
var realAry = [ ].slice.apply(wsz);
console.log(realAry);
</script>
注:
realAry = [ ].slice.call(wsz); <=> realAry = Array.prototype.slice.call(wsz);
- 简单解释一下伪数组:
伪数组最后有一个length,此length非这个数组的属性,而是这个数组的一个key,且值必须等于前边所有key的和,伪数组的遍历基本和普通数组相同。 - 普通数组对象里,length是数组的一个属性。
而上面的代码就借助我们的apply,把一个伪数组转换成了一个普通数组
-----------------------讲完了,溜了溜了??????----------------------