JavaScript中函数纪要(下)

在函数内部有着一个名叫arguments的类数组对象,内部包含着传入函数的所有参数,在arguments对象中,有一个名叫callee的属性,其作用可见下面这个阶乘的栗子:

1
2
3
4
5
6
7
8
9
10
function factorial(num){
if(num<=1){
return 1;
}else{
return num*factorial(num-1);
//这里也可以写成如下方式
return num*arguments.callee(num-1);
}
}
console.log(factorial(5))//120

可以看到的是,arguments.callee这个属性作为一个指针指向了拥有arguments对象的函数,也可以认为是当前正在执行的函数,而且可以消除与函数名factorial的耦合,不过值得注意的是,在ES5的严格模式下,调用arguments.callee方法会报错。

函数内部还有一个对象,就是我们所熟悉的this对象,this对象引用的是函数执行的环境对象,简单来说,this总是指向函数的直接调用者,而非间接调用者,在对象中,如果有new关键字,this指向new出来的那个对象。

在函数对象中,有个属性名为caller,这个属性作为一个引用,保存着调用当前函数的其他函数的引用,如下

1
2
3
4
5
6
(function(){
bar();//function (){bar();}
})();
function bar(){
console.log(bar.caller)//arguments.callee.caller
}

可以看到的是,匿名函数内部调用了bar函数,在bar函数内部,因为 bar.calller指向了调用bar的匿名函数,所以执行bar.caller就等于匿名函数的源代码,同callee一样的是,在ES5中访问caller属性会报错。

函数内部除了以上容易混淆的属性以外,还有些方法有时候也会让人比较困惑,例如call跟apply,show the code

1
2
3
4
5
6
7
8
9
10
11
function add(a,b){
return a+b;
}
function foo(a,b){
console.log(add.call(this,a,b))
}
function bar(a,b){
console.log(add.apply(this,[a,b]))
}
foo(20,20)//40
bar(20,20)//40

可以看到的是,call和apply两个方法作用都是在特定的作用域上面调用函数,换句话说,就是改变函数体内this指向,在foo和bar函数中,我们利用call和apply将函数的this值绑定到add函数上,因此它们便可以对内部的参数执行add函数里面的加法操作。

call和apply方法大作用致相同,不同的方法接受的参数,call必须要明确所有要处理的参数,也就是说,参数必须要逐个列举,而apply方法可以选择数组作为参数,因此可以在具体的环境中,看看是选择call还是apply方法。