callee
arguments对象有一个名为callee的属性,callee属性是一个指针,指向拥有这个arguments对象的函数。
比较经典的阶乘例子:
function factorial(num){ if(num <= 1){ return 1; }else{ return num * factorial(num - 1); } }
上述例子用到了递归,和函数名factorial紧密耦合到一起,可以使用arguments.callee解耦。
function factorial(num){ if(num <= 1){ return 1; }else{ return num * arguments.callee(num - 1); } }
caller
函数对象属性caller保存着调用当前函数的函数引用。如果在全局作用域中调用当前函数,它的值为null.
function outer(){
inner();
}
function inner(){
alert(inner.caller);
}
outer();
以上代码会导致警告框显示outer()函数的源代码。因为outer()调用inner(),所以inner.caller就指向了outer()。为了实现更松散的耦合,可以通过arguments.callee.caller来访问相同点信息。
function outer(){
inner();
}
function inner(){
alert(arguments.callee.caller);
}
outer();
函数的属性和方法
在ECMAScript中函数也是对象,因此函数有属性和方法,每个函数都有2个属性:length和prototype。其中,length表示函数希望接收的参数的个数,如下例:
function sayName(name){
console.info(name);
}
function sum(num1, num2){
return num1 + num2;
}
function sayHi(){
console.info("hi");
}
console.info(sayName.lenth); // 1
console.info(sum.length); // 2
console.info(sayHi.length); // 0
prototype
对于ECMAScript中的引用类型来说,prototype是保存它们所有实例方法的真正所在。如toString()和valueOf()等方法实际上都保存在prototype名下,只是通过各自对象的实例访问。在创建自定义引用类型以及实现继承时,prototype属性是非常重要的。prototype是不可枚举的,所以不能使用for-in。
call()和apply()方法
每个函数都包含2个非继承而来的方法:apply()和call()。这2个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
一、apply()方法接收2个参数:一个是在其运行函数的作用域,另一个是参数数组。参数数组可以是Array实例,也可以是arguments对象。
function sum(num1, num2){
return num1 + num2;
}
function callSum1(num1, num2){
return sum.apply(this,arguments);
}
function callSum2(num1, num2){
return sum.apply(this,[num1,num2]);
}
console.info(callSum1(10,10)); //20
console.info(callSum2(10,10)); //20
callSum1和callSum2函数传入this(这里实际上是window,因为这两个函数是在全局作用域中调用的)作为this的值。
二、call()方法和apply()方法的作用相同,它们区别只在于接收参数的方式不同,对于call()来说,第一个this值没有变化,变化的是其余参数都直接传递给参数。就是将传入的参数逐个列出来。
function sum(num1, num2){
return num1 + num2;
}
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
console.info(callSum(10,10)); //20
call()和apply()方法的强大之处不是传递参数,而是能够扩充函数赖以运行的作用域。看下面的例子
window.color = "red";
var o = {color:"blue"};
function sayColor(){
console.info(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue