自执行函数
JavaScript中任何库与框架设计的第一个要点就是解决命名空间与变量污染的问题。jQuery就是利用了JavaScript函数作用域的特性,采用自执行函数包裹了自身的方法来解决这个问题。从jQuery不同的版本中可以看出它的自执行函数有如下两种写法:
// 写法一
(function(window, factory) {
factory(window)
}(this, function() {
return function() {
//jQuery的调用
}
}));
// 写法二
(function(window, undefined) {
var jQuery = function() {}
// ...
window.jQuery = window.$ = jQuery;
})(window);
为了更好地理解它的做法,需要先强化一些函数的基本概念。
函数声明、函数表达式与匿名函数
- 函数声明:使用
function
关键字声明一个函数,再指定一个函数名,叫函数声明。
function funcName() {
...
}
- 函数表达式与匿名函数:未给函数命名,但最后将匿名函数赋予一个变量,叫函数表达式,否则就是匿名函数。因此匿名函数也属于函数表达式。
var funcName = function() {
...
}
- 区别:关于函数声明,它的一个重要特征就是函数声明提升,意思是在执行代码之前会先读取函数声明。这就意味着可以把函数声明放在调用它的语句后面;而函数表达式在调用之前需要赋值。
sayHi(); // It's OK
function sayHi() {
alert("Hi!");
}
sayHi(); // undefined!
var sayHi = function() {
alert("Hi!");
}
函数表达式 + ()
很重要的一点是:函数表达式后面通过加括号,能够立即调用该函数,而函数声明不可以!
function sayHi() {
alert("Hi!");
}() // illegal
var sayHi = function() {
alert("Hi!");
}() // legal
需要注意的是,匿名函数虽然属于函数表达式,但是在匿名函数后面加括号来直接调用是行不通的,因为JavaScript解释器将开头的function关键字当做函数声明,会报错:需要一个函数名
function() {
alert("Hi!");
}() // illegal : function name expected
如何正确告诉JavaScript解释器这是一个匿名函数,而不是一个函数声明
由于匿名函数这个会被误解的存在,因此需要想办法正确告诉JavaScript解释器这是一个匿名函数,即函数表达式,而不是一个函数声明。而这样的方法就是:在function前面加上!
、+
、-
、=
、()
等运算符,而这之中最安全的做法便是加()
!function() {
alert("Hi!");
}(); // legal
+function() {
alert("Hi!");
}(); // legal
-function() {
alert("Hi!");
}(); // legal
var sayHi = function() {
alert("Hi!");
}(); // legal
(function() {
alert("Hi!");
})(); // legal, notice
(function() {
alert("Hi!");
}()); // legal, notice
加别的运算符也许会引起不必要的麻烦,而加括号是很好的做法。注意上面最后的2个做法,代表了(function() {})();
、(function() {}());
。
回头来看jQuery的两种写法:
// 写法一
(function(window, factory) {
factory(window)
}(this, function() {
return function() {
//jQuery的调用
}
})); // 其实写法一可以看成是(function() {}());
// 写法二
(function(window, undefined) {
var jQuery = function() {}
// ...
window.jQuery = window.$ = jQuery;
})(window); // 而写法而可以看成是(function() {})();
所以这两种写法在自执行上本质是一样的。
采用这种写法的意义
JavaScript中没有私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉。根据JavaScript函数作用域链的特性,可以使用这种技术模仿一个私有作用域,把匿名函数作为一个“容器”,内部可以访问外部的变量,而外部环境不能访问内部。从而达到保护jQuery内部变量的作用。