= 立即执行函数

平常我们会看到以下一些函数形式的写法:

. (function(){ ... })()
. (function(){ ... }())
. !function(){}()

关于这样写是什么意思呢？有什么区别呢？

在此之前，我们需要了解几个概念：函数声明、函数表达式、匿名函数。

* 函数声明： function fnName() {...}
+
--
使用 function 关键字声明一个函数，再执行一个函数名，叫函数声明。
--
* 函数表达式： var fnName = function() { ... }
+
--
使用 function 关键字声明一个函数，但未给函数命名，最后将匿名函数赋予一个变量，叫函数表达式，这是最常见的函数表达式语法形式。
--
* 匿名函数： function() { ... }
+
--
使用 function 关键字声明一个函数，但未给函数命名，所以叫匿名函数，匿名函数属于函数表达式，匿名函数有很多作用，赋予一个变量则创建函数，赋予一个事件则成为事件处理程序或创建闭包等等。
--

函数声明和函数表达式不同之处在于:

. JavaScript 引擎在解析 JavaScript 代码时会“函数声明提升”当前执行环境(作用域)上的函数声明，而函数表达式必须等到 JavaScript 引擎执行到它所在行时，才会从上而下一行一行地解析函数表达式。
. 函数表达式后面可以加括号立即调用该函数，函数声明不可以，只能以 fnName() 形式调用。

知道了这些函数基本概念后，回头看看 (function(){ ... }()) 和 (function() { ... })() 这两种立即执行函数的写法，最初我以为是一个括号包裹匿名函数，并在后面加个括号立即调用函数，当时不知道为什么要加括号，后来明白，要在函数体后面加括号就能立即调用，则这个函数必须是函数表达式，不能是函数声明。

[source,javascript]
----
(function(a) {
  console.log(a);  //使用()运算符，打印出123
})(123);

(function(a) {
  console.log(a);  //使用()运算符，打印出1234
}(1234));

!function(a) {
  console.log(a);  //使用!运算符，打印出12345
}(12345);

+function(a) {
  console.log(a);  //使用+运算符，打印出123456
}(123456);

-function(a) {
  console.log(a);  //使用-运算符，打印出1234567
}(1234567);

var fn = function(a) {
  console.log(a);  //使用=运算符，打印出12345678
}(12345678);
----

可以看到输出结果，在 function 前面加 ! 、+ 、- 甚至是逗号等都可以起到函数定以后立即执行的效果，而 () 、! 、+ 、- 、= 等运算符，都将函数声明转换成函数表达式，消除了 javascript 引擎识别函数表达式和函数声明的歧义，告诉 javascript 引擎这是一个函数表达式，不是函数声明，可以在后面加括号，并立即执行函数的代码。

加括号是最安全的做法，因为 ! 、+ 、- 等运算符还会和函数的返回值进行运算，有时造成不必要的麻烦。

不过这样的写法有什么用呢？

javascript 中没有私有作用域的概念，如果在多人开发的项目上，你在全局或局部作用域中声明了一些变量，可能会被其他人不小心用同名的变量给覆盖掉，根据 javascript 函数作用域链的特性，可以使用这种技术可以模仿一个私有作用域，用匿名函数作为一个“容器”，“容器”内部可以访问外部的变量，而外部环境不能访问“容器”内部的变量，所以 (function(){ ... })() 内部定义的变量不会和外部的变量发生冲突，俗称“匿名包裹器”或“命名空间”。

jQuery 使用的就是这种方法，将 jQuery 代码包裹在 (function(window,undefined){ ...jquery代码...})(window) 中，在全局作用域中调用 jQuery 代码时，可以达到保护 jQuery 内部变量的作用。