关于arguments,caller以及匿名函数调用的问题
时间:2010-10-08 来源:岑安
此文内容不深,就当一个简单的笔记或总结吧。
先从arguments对象说起吧,[fnName.]arguments (其中fnName可选),为当前执行的 function 对象返回一个arguments 对象。
它有几个特性:
1.arguments是进行函数调用时,除了指定的参数外,还另外创建的一个隐藏对象。
2.arguments 对象只有函数开始时才可用。
3.arguments是一个类似数组但不是数组的对象,说它类似数组是因为其具有数组一样的访问性质及方式,可以由arguments[n]来访问对应的单个参数的值,并拥有数组长度属性length。
4.还有就是arguments对象存储的是实际传递给函数的参数,而非函数声明所定义的参数列表
5.不能显式创建 arguments 对象。
下面就3,4两点举两个简单的例子:
function testArg(a,b){ alert(arguments.length);//实际参数个数 alert(testArg.length); //声明参数个数 arguments.length != 0&&alert(arguments[0]); //可以通过类似Array的方式访问arguments内部元素 }
您可以尝试运行下面的测试:
testArg()
当我们传一个参数时: testArg(true)
正因为上面的原因,所以我们说它是类似于Array的对象,但是它确实又不是Array对象。
//arguments类似Array,但又不是Array Array.prototype.self = "me!" alert((new Array()).self); //me! (function(){ alert(arguments.self); //undefined })();
同样,也可以用instanceof来判断:
(function(){ alert(arguments instanceof Object); //true alert(arguments instanceof Array); //false })()
下面说说caller,它是用于返回对一个函数的“引用”,表明函数调用了该函数,它有以下几个特性:
1.caller 属性只有在函数执行时才有定义。
2.如果函数是由顶层调用的,那么 caller 包含的就是 null 。
3.如果在函数上下文中使用 caller 属性,那么结果和 functionName.toString 一样,也就是说,显示的是函数的反编译文本。
/* == caller ==*/ function testCaller(){ //fnName.caller if(testCaller.caller){ alert(testCaller.caller); } else {alert('top function')} }
在函数上下文中调用看看:(function(){testCaller()})()
可以看到,结果为调用testCaller()的函数,也就是外层匿名函数的反编译文本。
恩,下面接着说arguments的另一个属性callee,(貌似叙述顺序有点乱,不好意思),说callee之前,咱们还是先说说匿名函数。
function fn1(){alert('one')} var fn1 = function(){alert('one')}
这两种方式有什么区别?
1 第一种方式定义函数,函数声明过程在整个程序执行之前的预处理就完成了,所以只要处于同一个作用域,就可以访问到,即使在定义之前调用它也可以。
2 第二种方式通过匿名函数创建,这种方式函数只能按照程序流程执行到定义的那一行代码才被声明,所以只能在定义之后调用它。
大家也看到了,上面举的几个小例子里就用了匿名函数。
现在问题出来了,我们知道,函数是可以递归调用的,比如我们做个高斯很小就会的题目:1+2+3+...+99+100?
这个问题就可以用递归做:
function sum(n){ return n>0?n+sum(n-1):n; } alert(sum(100));
大家可以看下结果是不是当前高斯算出来的5050. run
那如果是匿名函数的递归调用呢?这是就需要刚刚提到的callee属性了。回到callee上来
callee 属性的初始值就是正被执行的 Function 对象,它返回的是当前正在执行的函数正文。
callee 属性是 arguments 对象的一个成员,它表示对函数对象本身的引用,这有利于匿名函数的递归或者保证函数的封装性。
下面就借助callee用匿名函数改写上面的简单例子:
(function(n){ return n>0?n+arguments.callee(n-1):n })(100);
这种方式是不是要优雅的多,而且比起直接用函数名作递归调用,这种方式更好的体现了是在调用自身。
恩,此文差不多就到这儿吧...