函数内部,有两个特殊的对象:arguments和this。
一、arguments
arguments的作用是保存传入函数中的所有参数,而且这个arguments有一个名叫callee的属性,这个属性是一个指针,指向拥有arguments对象的函数。
举个递归算法---阶乘函数的例子:
function factorial(num){ if(num<=1){ return 1; }else{ return num*factorial(num-1) } }
上面的代码,在函数有名字,而且名字以后也不会变的情况下,这样定义没有啥问题。不过,这样这个函数的执行与函数名紧紧耦合在一起了。所以为了消除这种紧密耦合,我们可以使用arguments.callee。
function factorial(num){ if(num<=1){ return 1; }else{ return num*arguments.callee(num-1) } }
这样,无论引用函数时使用什么名字,都可以保证正常完成递归调用。举个例子:
var trueFactorial = factorial; factorial = function(){ return 0; } console.log(trueFactorial(5)); //120 console.log(factorial(5)); //0
变量trueFactorial获得了factorial的值,实际上是在另一个位置上保存了一个函数的指针。然后我们又把一个返回0的函数赋值给factorial变量。如果像原来的factorial()不使用arguments.callee,那么调用trueFactorial()就会返回0。但是,在解除了耦合之后,trueFactorial()依然能够正常的计算阶乘。而再次被赋值的factorial(),只能按照重新赋给的值进行计算。
严格模式下,不能通过脚本访问arguments.callee,可以使用命名函数表达式来达成相同的结果,栗如:
var factorial = (function f(num){ if (num <= 1){ return 1; } else { return num * f(num-1); } });
以上代码创建了一个名为f()的命名函数表达式,然后将它赋值给变量factorial。即便把函数赋值给了另一个变量,函数的名字f依然有效。这种方式在严格模式和非严格模式下都行得通。
二、this
this引用的是函数执行的环境对象,当在网页的全局作用域中调用函数时,this对象引用的就是window。
window.color = "red"; var o = { color: "blue"}; function sayColor(){ console.log(this.color); } sayColor(); // "red" o.sayColor = sayColor; o.sayColor(); // "blue"
函数sayColor()在全局作用域中定义,并且引用了this对象。在这个函数调用之前,this的值并不确定,所以this可能在代码执行过程中引用不同的对象。
当在全局作用域调用sayColor(),那么this引用的是全局对象window,换句话说就是,对this.color求值会转换成对window.color求值,所以结果就是“red”。
当把这个函数赋值给对象o并调用o.sayColor()时,this引用的是对象o,因此对this.color求值会转换成对o.color求值,所以结果就是“blue”。
注:函数的名字仅仅是一个包含指针的变量,所以在不同的环境下执行,全局的sayColor()函数与o.sayColor()指向的仍然是同一个函数。
参考资料
《javascript高级程序设计(第3版)》第5章 引用类型