你是否曾经看过 jQuery 1.4 源代码的内部结构并注意到它是如何被封装的:
(function( window, undefined ) {
//All the JQuery code here
...
})(window);
我读过一篇关于 JavaScript 命名空间 的文章和另一篇名为 "括号的重要配对" 的文章,所以我了解一些这里的情况。
但我从未见过这种特殊的语法。那个undefined
在那里做什么?为什么需要传递 window
,然后再次出现在最后?
你是否曾经看过 jQuery 1.4 源代码的内部结构并注意到它是如何被封装的:
(function( window, undefined ) {
//All the JQuery code here
...
})(window);
我读过一篇关于 JavaScript 命名空间 的文章和另一篇名为 "括号的重要配对" 的文章,所以我了解一些这里的情况。
但我从未见过这种特殊的语法。那个undefined
在那里做什么?为什么需要传递 window
,然后再次出现在最后?
undefined是一个普通变量,可以通过 undefined = "new value";
简单地更改。因此,jQuery创建了一个本地的“undefined”变量,它真正的值是未定义的。
window变量被局部化以提高性能。因为当JavaScript查找一个变量时,它首先通过局部变量,直到找到变量名。如果没有找到,则JavaScript继续查找下一个范围,直到过滤出全局变量。因此,如果将window变量局部化,JavaScript可以更快地查找它。 更多信息:Speed Up Your JavaScript - Nicholas C. Zakas
未定义
通过将undefined
声明为参数,但从未传递值给它,确保它始终是未定义的,因为它只是全局范围内可以被覆盖的变量。这使a === undefined
成为typeof a == 'undefined'
的安全替代品,可以节省一些字符。它还使代码更易于缩小,因为例如undefined
可以缩短为u
,从而再次节省了几个字符。
窗口
将window
作为参数传递会在本地范围内保留一个副本,这会影响性能:http://jsperf.com/short-scope。现在,对window
的所有访问都必须向上移动一个级别。与undefined
一样,再次使用本地副本可进行更激进的缩小。
附注:
虽然这可能不是jQuery开发人员的意图,但传递window
使该库更容易集成到服务器端JavaScript环境中,例如node.js - 在那里没有全局window
对象。在这种情况下,只需要更改一行即可将window
对象替换为另一个对象。对于jQuery而言,可以创建一个虚拟的window
对象并传递给它,以便进行HTML抓取(例如jsdom这样的库可以做到这一点)。
(function(a,b){})(window);
-- a
和 b
比 window
和 undefined
短得多 :) - gnarfthis
传递给 IIFE,而不是 window
,以在浏览器和 node.js 环境中获取全局作用域。例如:(function (root, undefined) { 'use strict'; var $ = root.jQuery; }(this));
- Lars Gyrup Brink Nielsen其他人已经解释了undefined
。 undefined
就像是一个全局变量,可以重新定义为任何值。这种技术的目的是防止所有未定义检查中断,如果有人在某处写了undefined = 10
,那么这个技巧就能防止出现问题。一个从未传递的参数一定是真正的undefined
,无论变量undefined
的值是什么。
传递window的原因可以通过以下示例说明。
(function() {
console.log(window);
...
...
...
var window = 10;
})();
控制台会输出什么?是 window
对象的值吗?不对!是 10 吗?也不对!它会输出 undefined
。JavaScript 解释器(或者 JIT 编译器)会将其重写为:
(function() {
var window; //and every other var in this function
console.log(window);
...
...
...
window = 10;
})();
然而,如果您将window
变量作为参数获取,则没有var
,因此不会有任何意外情况。
我不知道jQuery是否在这样做,但是如果您出于任何原因在函数中重新定义了window
局部变量,从全局范围中继承它是个好主意。
window
这样传递是为了防止有人在IE中重新定义窗口对象,我想undefined
也是同样的原因,以防它被后续某种方式重新分配。
脚本中的顶级window
只是给参数“window”命名,这个参数比全局window
引用更本地,并且它是这个闭包内部使用的。末尾的window
实际上指定了要传递的第一个参数,在这种情况下是window
的当前含义......希望在此之前您还没有破坏window
。
可能更容易理解的方法是展示jQuery中最典型的用例,即插件.noConflict()
处理,因此对于大多数代码,即使在此范围之外,$
仍然可以使用,即使它表示的是除jQuery
之外的其他东西:
(function($) {
//inside here, $ == jQuery, it was passed as the first argument
})(jQuery);
进行了1000000次迭代测试。这种本地化对性能没有影响。在1000000次迭代中,甚至没有花费一毫秒的时间。这是毫无用处的。
window
,那么通过本地副本可能会看到一些收益。 - gabereal