1. 什么是匿名函数

匿名函数是没有名字的函数,其用途非常之多,先看看以下的例子:

function myfunc(arg) {
}
var myfunc = function(arg) {
}

这两种定义在逻辑上等价,但是还是有区别。前者会在代码执行前被加载到作用域中,后者在代码执行到那一行时才会有定义。还有一个区别是函数声明会给函数指定一个名字,而函数表达式则是创建一个匿名函数,再将这个匿名函数赋给一个变量。

也可以像如下例子写一个匿名函数:

function(arg) {
}

这段代码完全有效,但问题是无法调用这个函数,因为没有指向这个函数的指针,不过,在将函数作为参数传入另一个函数,或从一个函数中返回另一个函数时,通常都要使用这种形式来定义匿名函数。


2. 递归

递归函数即函数通过名字调用自身,例如:

function factorial(num) {
  if (num <= 1) {
    return 1;
  }
  else {
    return num * factorial(num-1);
  }
}

但以下代码可能会导致问题:

var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(10)); //错误

以上代码把factorial()函数保存在变量anotherFactorial中,然后将factorial变量设置为null,但在接下来调用anotherFactorial()时,由于必须执行factorial(),因此就会导致错误。而arguments.callee是一个指向正在执行的函数的指针,因此可以用它实现递归,例如:

function factorial(num) { 
  if (num <= 1) { 
    return 1; 
  } 
  else { 
    return num * arguments.callee(num-1); 
  } 
}


3. 闭包

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数,例如:

function myfunc(arg) {
  return function(myobj) {
    var value = myobj[arg];
  }
}

在这个例子中,匿名函数访问了外部函数中的变量arg,即使这个内部函数被返回了,而且在其他地方被调用了,但它仍然可以访问变量arg。之所以还能够访问这个变量,是因为内部函数的作用域链中包含myfunc()的作用域。


4. 模仿块级作用域

JavaScript没有块级作用域,例如:

function myfunc(count) {
  for (var i=0; i<count; i++) {
    alert(i);
  }
  var i;
  alert(i);
}

在JavaScript中,变量i从有定义开始,在函数内部可随处访问,即使错误地重新声明同一个变量。JavaScript不会告诉是否多次声明了同一个变量,它会对后续的声明视而不见(但是会执行后续声明中的初始化)。匿名函数可以用来模仿块级作用域,例如:

(function() {
})();

以上代码定义并立即调用一个匿名函数,将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式,跟随其扣的另一对圆括号会立即调用这个函数。因此以上例子可以改写为:

function myfunc(count) { 
  (function() {
    for (var i=0; i<count; i++) { 
      alert(i); 
    } 
  })();
  alert(i); //错误
}