1.3 自调用匿名函数
从代码清单1-1中还可以看到,jQuery的所有代码都被包裹在了一个立即执行的匿名函数表达式中,这种代码结构称为“自调用匿名函数”。当浏览器加载完jQuery文件后,自调用匿名函数会立即开始执行,初始化jQuery的各个模块。相关代码如下所示:
(function( window, undefined ) {
var jQuery = ...
//
...
window.jQuery = window.$ = jQuery;
})(window);
上面这段代码涉及一些JavaScript基础知识和编码习惯,下面以提问的方式来逐一分析。
1)为什么要创建这样一个自调用匿名函数?
通过创建一个自调用匿名函数,创建了一个特殊的函数作用域,该作用域中的代码不会和已有的同名函数、方法和变量以及第三方库冲突。由于jQuery会被应用在成千上万的JavaScript程序中,所以必须确保jQuery的代码不会受到其他代码的干扰,并且jQuery不能破坏和污染全局变量以至于影响到其他代码。这一点是任何一个JavaScript库和框架所必须具备的功能。
注意,在这个自调用匿名函数的最后,通过手动把变量jQuery添加到window对象上,明确地使变量jQuery成为公开的全局变量,而其他的部分将是私有的。
另外,自调用匿名函数还可以有两种等价的写法,如下所示(注意加了底纹的圆括号的位置):
// 写法1(常见写法,也是
jQuery 所采用的)
( function() {
//
...
} )();
// 写法2
( function() {
//
...
}() );
// 写法3
!function() {
//
...
}();
2)为什么要为自调用匿名函数设置参数window,并传入window对象?
通过传入window对象,可以使window对象变为局部变量(即把函数参数作为局部变量使用),这样当在jQuery代码块中访问window对象时,不需要将作用域链回退到顶层作用域,从而可以更快地访问window对象,这是原因之一;另外,将window对象作为参数传入,可以在压缩代码时进行优化,在压缩文件jquery-1.7.1.min.js中可以看到下面的代码:
(function(a,b){ ... })(window);
// 参数 window 被压缩为 a,参数 undefined
被压缩为 b
3)什么要为自调用匿名函数设置参数undefined?
特殊值undefined是window对象的一个属性,例如,执行下面的代码将会弹出true:
alert( "undefined" in window );
// true
通过把参数undefined作为局部变量使用,但是又不传入任何值,可以缩短查找undefined时的作用域链,并且可以在压缩代码时进行优化,如前面代码所示,参数undefined会被压缩为b。
另外,更重要的原因是,通过这种方式可以确保参数undefined的值是undefined,因为undefiend有可能会被重写为新的值。可以用下面的代码来尝试修改undefined的值,在各浏览器中的测试结果见表1-1。
undefined = "now it's defined";
alert( undefined );
表1-1 在浏览器中尝试修改
undefined 的值
浏 览 器 测 试 结 果 结 论
IE 6.0、IE 7.0、IE 8.0 now it's defined 可以改变
IE 9.0、IE 10.0 undefined 不能改变
Chrome 16.0.912.77 now it's defined 可以改变
Chrome 17.0.963.56 undefined 不能改变
Firefox 3.6.28 now it's defined 可以改变
Firefox 4.0 undefined 不能改变
Safari 4.0.2 now it's defined 可以改变
Safari 4.0.4 undefined 不能改变
Opera 11.52 now
it's defined 可以改变
Opera 11.60 undefined 不能改变
4)注意到自调用匿名函数最后的分号(;)了吗?
通常在JavaScript中,如果语句分别放置在不同的行中,则分号(;)是可选的,但是对于自调用匿名函数来说,在之前或之后省略分号都可能会引起语法错误。例如,执行下面的两个例子,就会抛出异常。
例1 在下面的代码中,如果自调用匿名函数的前一行末尾没有加分号,则自调用匿名函数的第一对括号会被当作是函数调用。
var n = 1
( function(){} )()
// TypeError: number is not a function
例2 在下面的代码中,如果未在第一个自调用匿名函数的末尾加分号,则下一行自调用匿名函数的第一对括号会被当作是函数调用。
( function(){} )()
( function(){} )()
// TypeError: undefined is not a function
所以,在使用自调用匿名函数时,最好不要省略自调用匿名函数之前和之后的分号。