立即执行函数

了解完函数之后,我们介绍一下立刻调用函数,也就是我们常说的自执行函数。

什么是自执行

在JavaScript中,每个函数被调用时都会创建一个全新的上下文环境。因此,在函数内部定义的变量和函数就只能在函数内部访问,在外部无法访问,那么在该上下文环境中,调用的函数就提供了一个非常方便的方式来创建私有成员。也就是解释了JavaScript的作用域是function-level

  1. function makeCounter() {
  2. // 只能在makeCounter内部访问i
  3. var i = 0;
  4. return function () {
  5. console.log(++i);
  6. };
  7. }

原理

一个表达式后面加上括号(),该表达式会立即执行, 但是在一个语句后面加上括号(),是完全不一样的意思,他的只是分组操作符。

  1. /*
  2. * 下面两种声明函数的方式,使用函数引用加上括号,可以立即调用函数
  3. */
  4. var foo = function() {};
  5. function foo() {};
  6. /*
  7. * 解析器解析全局的function或者function内部function关键字的时候,默认是认为function声明,而不是function表达式,如果你不显示告诉编译器,它默认会声明成一个缺少名字的function
  8. * 所以在函数声明后面直接加上括号,会出现语法错误。
  9. */
  10. function (){ /* code */ }(); //期望是立即调用一个匿名函数表达式,结果是进行了函数声明,函数声明必须要有标识符做为函数名称。
  11. /*
  12. * 即使是函数声明,直接在后面使用括号,也不会立即执行。后面的括号相当于在函数声明后面加上分号,并且断行。后面的括号是分组运算符
  13. */
  14. function foo(){}(); // 分组运算符需要表达式

理解了这些,我们只需要用大括弧将代码的代码全部括住就行了,因为JavaScript里括弧()里面不能包含语句,所以在这一点上,解析器在解析function关键字的时候,会将相应的代码解析成function表达式,而不是function声明。

所以那些匿名函数附近使用括号或一些一元运算符的惯用法,就是来引导解析器,指明运算符附近是一个表达式。

实现方式

  1. // 下面2个括弧()都会立即执行
  2. (function () { /* code */ } ()); // 推荐使用这个
  3. (function () { /* code */ })(); // 但是这个也是可以用的
  4. // 由于括弧()和JS的&&,异或,逗号等操作符是在函数表达式和函数声明上消除歧义的
  5. // 所以一旦解析器知道其中一个已经是表达式了,其它的也都默认为表达式了
  6. // 不过,请注意下一章节的内容解释
  7. var i = function () { return 10; } ();
  8. true && function () { /* code */ } ();
  9. 0, function () { /* code */ } ();
  10. // 如果你不在意返回值,或者不怕难以阅读
  11. // 你甚至可以在function前面加一元操作符号
  12. !function () { /* code */ } ();
  13. ~function () { /* code */ } ();
  14. -function () { /* code */ } ();
  15. +function () { /* code */ } ();
  16. // 还有一个情况,使用new关键字,也可以用,但我不确定它的效率
  17. // http://twitter.com/kuvos/status/18209252090847232
  18. new function () { /* code */ }
  19. new function () { /* code */ } () // 如果需要传递参数,只需要加上括弧()

自执行匿名函数和立即执行的函数表达式区别

自执行,顾名思义,自己执行自己的操作:

  1. // 这是一个自执行的函数,函数内部执行自身,递归
  2. function foo() { foo(); }
  3. // 这是一个自执行的匿名函数,因为没有标示名称
  4. // 必须使用arguments.callee属性来执行自己
  5. var foo = function () { arguments.callee(); };
  6. // 这可能也是一个自执行的匿名函数,仅仅是foo标示名称引用它自身
  7. // 如果你将foo改变成其它的,你将得到一个used-to-self-execute匿名函数
  8. var foo = function () { foo(); };

我们倡导的说法是立即执行函数,所以自执行其实也是一个立即执行函数。

  1. // 有些人叫这个是自执行的匿名函数(即便它不是),因为它没有调用自身,它只是立即执行而已。
  2. (function () { /* code */ } ());
  3. // 为函数表达式添加一个标示名称,可以方便Debug
  4. // 但一定命名了,这个函数就不再是匿名的了
  5. (function foo() { /* code */ } ());
  6. // 立即调用的函数表达式(IIFE)也可以自执行,不过可能不常用罢了
  7. (function () { arguments.callee(); } ());
  8. (function foo() { foo(); } ());

参考资料

原文: https://leohxj.gitbooks.io/front-end-database/content/javascript-basic/immediately-invoked-function-expression.html