JavaScript / jQuery语法如何工作:(function(window,undefined){})(window)?


153

您是否曾经看过jQuery 1.4源代码,并注意到它是如何通过以下方式封装的:

(function( window, undefined ) {

  //All the JQuery code here 
  ...

})(window);

我已经读过一篇有关JavaScript名称空间的文章,另一篇文章是“ 重要的一对Parens ”,所以我知道这里发生了什么。

但是我以前从未见过这种特殊的语法。那undefined在那里做什么?为什么window需要通过传递然后再次出现在末尾?


3
我想补充一下,保罗·爱尔兰(Paul Irish)在此视频中谈到了这一点:paulirish.com/2010/10-things-i-learned-from-the-jquery-source
Mottie'2

@Bergi,您将我的问题标记为另一个问题的重复,但是在重复之前将近一年我问了这个问题。演员阵容应该与之相反。
dkinzer 2014年

@dkinzer:这不是关于早些被问到,而是关于最高质量的答案。诚然,在这种情况下这是并驾齐驱的,但是我发现CMS的答案是最好的答案。不过请访问chat.stackoverflow.com/rooms/17进行讨论
Bergi 2014年

Answers:


161

undefined是一个普通变量,可以使用轻松更改undefined = "new value";。因此,jQuery创建了一个真正未定义的本地“未定义”变量。

由于性能原因,将window变量设为本地变量。因为当JavaScript查找变量时,它首先会遍历局部变量,直到找到变量名为止。当找不到它时,JavaScript将遍历下一个范围等,直到它对全局变量进行过滤。因此,如果将window变量设为本地,JavaScript可以更快地查找它。更多信息:加速您的JavaScript-Nicholas C. Zakas


1
谢谢!这是一个非常有用的视频。我认为您需要编辑您的回答以说“ window变量设为本地”。我确实认为这是最好的答案。我数了一下,发现在JQuery源代码中窗口对象至少被调用了17次。因此,将窗口移到“本地合并范围”中将产生重大影响。
dkinzer

@DKinzer哦,是的,您当然知道了,它是本地化的。很高兴能为您提供帮助=)
Vincent 2010年

1
根据此更新的测试jsperf.com/short-scope/5,通过jQuery获取窗口常量时,传递“窗口”的速度实际上较慢,这对Safari尤其不利。因此,尽管“未定义”有一个用例,但我不太相信“窗口”在所有情况下都特别有用。
hexalys 2013年

1
这对于减少代码也有积极作用。因此,minifyer可以用较短的名称替换“ window”和“ undefined”的所有用法。
TLindig

2
@ lukas.pukenis当您要建立一个数百万个网站使用的图书馆时,明智的做法是不要对疯子的行为做出假设。
JLRishe 2014年

54

未定义

通过声明undefined为参数但不传递任何值,可以确保始终是未定义的,因为它只是全局范围内的变量,可以被覆盖。这是a === undefined的安全替代方案typeof a == 'undefined',可节省一些字符。它还可以使代码更易于使用,例如undefined可以简化u为节省更多字符。

窗口

window作为参数传递会将副本保留在本地范围内,这会影响性能:http : //jsperf.com/short-scopewindow现在,对的所有访问都必须比作用域链少一层。与一样undefined,本地副本再次允许更积极的缩小。


边注:

尽管这并不是jQuery开发人员的意图,但传入window可以使该库更轻松地集成到服务器端Javascript环境中,例如,没有全局对象的node.js。window在这种情况下,只需更改一行即可window用另一行替换该对象。在jQuery的情况下,window可以创建一个模拟对象并将其传递给HTML抓取对象(jsdom之类的库可以做到这一点)。


2
+1提及Minification,希望我能为jsperf +2-简化版本是(function(a,b){})(window);- a并且bwindowand 短得多undefined:)
gnarf

+1我之前听说过作用域链,但忘记了该术语。谢谢!
alex

这也是使用void(0)的另一种选择,它的意思是出于相同的原因:void(0)是获取未定义值的安全方法。
glmxndr 2011年

“你说的”是参照本的其他问题:stackoverflow.com/questions/4945265/...
gnarf

@gnarf,感谢您提出问题合并的通知。我将编辑答案以使其更有意义;)
David Tang

16

其他人已经解释了undefinedundefined就像可以重新定义为任何值的全局变量。如果有人在undefined = 10某处写了话,此技术可防止所有未定义的检查都被破坏。undefined无论变量 的值如何,永远不会传递的参数保证是实数undefined

可以通过以下示例说明通过窗口的原因。

(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出于某种原因要在函数中的任何位置重新定义局部变量,则最好从全局范围中借用它。


6

window就像这样传入,以防万一有人决定在IE中重新定义window对象,我假设对于undefined,也是如此,以防以后以某种方式重新分配它。

window脚本的顶部只是命名参数“ window”,该参数比全局window引用更本地化,并且此闭包中的代码将使用什么。将window在年底实际上指定什么传递的第一个参数,在这种情况下,目前的意义window......希望是你还没有搞砸了window在这之前发生。

这可以通过显示在jQuery中使用的最典型的案例,插件想更容易.noConflict()处理,所以对于广大的代码,你仍然可以使用$,即使这意味着什么其他jQuery这个范围之内:

(function($) {
  //inside here, $ == jQuery, it was passed as the first argument
})(jQuery);

谢谢。这很有道理。不过,我认为答案是这与@ C.Zakas说的结合。
dkinzer

@DKinzer恐怕我不是C. Zakas;)
Vincent

@Vincent,对此表示抱歉:-)
dkinzer

5

经过1000000次迭代测试。这种本地化对性能没有影响。1000000次迭代中甚至没有一个毫秒。这根本没用。


2
更不用说,闭包包含所有变量以及函数实例,因此,如果闭包中有100个字节,而1000个实例中有100kb的更多内存。
Akash Kava

@AkashKava和@Semra,我认为此测试不能真正解释为什么您会在现实世界中通过窗口。仅当您具有深度嵌套的闭包(沿范围链进一步访问该变量)时,才能看到性能提升。显然,如果您的函数距离变量范围链仅一步之遥,那么您将不会获得太多收益。但是如果它在那儿是waaaay,并且需要获取,window您可能会发现本地副本有所帮助。
gabereal 2014年

@gabereal新的JavaScript引擎会在编译时自行解决闭包,在运行时闭包不会提高性能。我怀疑是否会有可衡量的性能提升,如果您如此有信心,可以创建jsperf示例来演示性能。我们获得的唯一性能是隔离闭包,从而实现更好的缩小效果,从而减小了大小,并通过更快的脚本下载和解析实现了性能。
Akash Kava 2014年

@AkashKava是什么让你觉得我这么有信心?我只是说“可能会有所收获”和“我不认为”。我可以创建一个测试,但是我不愿意,因为我从不使用这种模式。您能否解释“在编译时解析闭包”的含义?而不是什么选择?Javascript引擎之前的功能有何不同?
gabereal 2014年

找到一个代码,该代码在最后固化窗口,但未在函数的参数中输入,这是什么意思?它确保即使我假设没有任何窗口参数,参数也可以遵循原始全局范围对象。
Webwoman
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.