之前在看《JavaScript高级程序设计》第十三章P391看到关于arguments.callee的用法, 然后又翻回之前的内容, 原来在第5章引用类型P113的时候曾经讲到过这个arguments.callee, 发现这个arguments对象的属性其实有挺大的用处的, 接下来讲一讲的两大作用:
作用一. 取消代码与函数名的耦合状态
首先, 如果要定义一个阶乘函数, 大多数人都会这样写:
- function factorial(num){
- if( num <= 1){
- return 1;
- }
- else{
- return num * factorial (num - 1);
- }
- }
这种写法确实可以实现效果, 但问题是这个函数的执行与函数名factorial紧紧耦合在了一起, 而且在如下代码调用的时候会出现错误:
- var trueFactorial = factorial;
- factorial = function(){
- return 0;
- };
- alert(trueFactorial(5)); //0
- alert(factorial(5)); //0
在此, 变量trueFactorial获得了factorial的值, 实际上实在另一个位置保存了一个函数的指针. 然后,我们又将一个简单地返回0的函数赋值为factorial变量. 那么当第5行调用了trueFactorial时, 走到上一块代码中方法定义的第6行, return num * factorial (num - 1); 那么此时factorial( num - 1)返回的结果就是0了, 那么此时这个 factorial方法就不再有计算阶乘的功能了, 那么如何解决这个问题呢? 可以像下面这样重新定义factorial方法:
- function factorial(num){
- if(num <= 1){
- return 1;
- }
- else{
- return num * arguments.callee(num - 1);
- }
- }
那么现在执行如下代码时的结果就不同了:
- var trueFactorial = factorial;
- factorial = function(){
- return 0;
- };
- alert(trueFactorial(5)); //120
- alert(factorial(5)); //0
为什么呢? 首先trueFactorial = factorial的意思并不是让trueFactorial指向factorial, 而是把factorial引用的函数传递给trueFactorial, 所以说此时trueFactorial的方法定义如下面这样:
- if(num <= 1){
- return 1;
- }
- else{
- return num * arguments.callee(num - 1);
- }
因此, 当代码运行到第5行时, 不会像以前那样执行那个return 0的factorial函数了, 而是通过arguments.callee执行当前这个作用于中arguments对象所属的那个函数, 也就是trueFactorial !! 由于此时trueFactorial的方法定义仍然是一个计算阶乘的函数, 因此当传入参数为5时返回的结果就是120! 可以看到arguments.callee的作用是解除了函数体内代码与函数名的耦合状态, 从而实现在函数名发生变化或者函数的定义发生变化时不会受到影响.
作用二. 取消匿名函数的绑定
以前当我们通过addEventListener或者attachEvent等方法为元素绑定事件处理程序时, 如果绑定的是匿名函数, 那么在正常情况下是没有办法解除绑定的, 也就是说没办法取消事件绑定, 那么假如我们通过如下方式来绑定事件处理程序:
- var num = 1;
- var oDiv = document.getElementById('myDiv');
- oDiv.addEventListener( ' click ' , function(){
- alert(num);
- num++;
- if( num == 3 ){
- oDiv.removeEventListener( 'click', arguments.callee);
- }
- });
如果像上面这样执行代码时, 可以发现当我点击id为myDiv的元素两次时, 会分别弹出警告框提示1、2, 然而点击第3次的时候就不再弹框了, 原因在于通过第7行解除了事件绑定, 因为arguments.callee指向的是当前作用域下arguments对象所属的函数, 此时是那个被绑定的匿名函数, 因此通过removeEventListener就可以解除匿名函数的绑定了!!
通过这两个例子我们可以发现arguments.callee可以做到平常在正常情况下做不到的事情, 所以这个属性我觉得还挺有用的.