这个 JavaScript/jQuery 语法是如何工作的:(function(window, undefined) )(window)?

Posted

技术标签:

【中文标题】这个 JavaScript/jQuery 语法是如何工作的:(function(window, undefined) )(window)?【英文标题】:How does this JavaScript/jQuery syntax work: (function( window, undefined ) )(window)?这个 JavaScript/jQuery 语法是如何工作的:(function(window, undefined) )(window)? 【发布时间】:2011-02-12 13:12:40 【问题描述】:

你有没有看过 jQuery 1.4 源代码的底层并注意到它是如何以下列方式封装的:

(function( window, undefined ) 

  //All the JQuery code here 
  ...

)(window);

我读过一篇关于javascript Namespacing 的文章和另一篇名为“An Important Pair of Parens”的文章,所以我对这里发生的事情有所了解。

但我以前从未见过这种特殊的语法。 undefined 在那里做什么?而为什么window需要通过,然后又出现在最后?

【问题讨论】:

我想补充一点,Paul Irish 在这段视频中谈到了这一点:paulirish.com/2010/10-things-i-learned-from-the-jquery-source @Bergi 您将我的问题标记为另一个问题的副本,但我在副本前将近一年问了这个问题。演员应该是相反的。 @dkinzer:这与被问早无关,而是关于最高质量的答案。诚然,在这种情况下,它是并驾齐驱的,但我发现 CMS 的答案是最好的。不过来chat.***.com/rooms/17讨论这个吧 另见What advantages does using (function(window, document, undefined) … )(window, document) confer? 【参考方案1】:

undefined 是一个普通变量,可以简单地使用undefined = "new value"; 进行更改。所以 jQuery 创建了一个真正未定义的本地“未定义”变量。

出于性能原因,window 变量是本地变量。因为 JavaScript 在查找变量时,首先会遍历局部变量,直到找到变量名。如果没有找到,JavaScript 会遍历下一个作用域等,直到它过滤掉全局变量。因此,如果 window 变量是本地的,JavaScript 可以更快地查找它。 更多信息:Speed Up Your JavaScript - Nicholas C. Zakas

【讨论】:

谢谢!那是一个内容丰富的视频。我认为你需要编辑你的回复说“窗口变量是本地的”。我认为这是最好的答案。我数了数,发现在 JQuery 源码中,window 对象至少被调用了 17 次。因此,将窗口移动到本地作用域中一定会产生显着影响。 @DKinzer 哦,是的,你是对的,当然它是本地化的。很高兴能帮到你=) 根据这个更新的测试jsperf.com/short-scope/5 通过 'window' 实际上更慢,当它通过 jQuery 获取窗口常量时,对 Safari 尤其不利。因此,虽然“未定义”有一个用例,但我不太相信“窗口”在所有情况下都特别有用。 它对缩小代码也有积极的作用。所以“window”和“undefined”的每一个用法都可以被缩小器替换为一个更短的名字。 @lukas.pukenis 当你正在创建一个在数百万个网站中使用的库时,明智的做法是不要假设疯子会做什么。【参考方案2】:

未定义

通过将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 等库可以做到这一点)。

【讨论】:

+1 用于提及 Minification ,希望我可以为 jsperf +2 -- 缩小版是 (function(a,b))(window); -- abwindow 和 @987654341 短得多@ :) +1 我以前听说过作用域链,但忘记了这个词。谢谢! 这也是使用 void(0) 的替代方法,其原因相同:void(0) 是获取未定义值的安全方法。 "What you say" 指的是另一个问题:***.com/questions/4945265/… @gnarf,感谢您通知问题已合并。我将编辑我的答案以使其更有意义;)【参考方案3】:

其他人已经解释了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 局部变量,最好从全局范围借用它。

【讨论】:

【参考方案4】:

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

该脚本中的顶部window 只是将参数命名为“窗口”,这是一个比全局window 引用更本地化的参数,它也是这个闭包内的代码将使用的。最后的 window 实际上是指定第一个参数要传递的内容,在这种情况下,window 的当前含义...希望在此之前你还没有搞砸 window

通过显示 jQuery 中使用的最典型案例,插件.noConflict() 处理可能更容易想到,因此对于大多数代码您仍然可以使用$,即使它意味着什么其他 比jQuery 超出此范围:

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

【讨论】:

谢谢。这很有意义。不过,我认为答案是这个和@C.Zakas 所说的结合。 @DKinzer 恐怕我不是 C. Zakas ;) @Vincent,对此感到抱歉:-)【参考方案5】:

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

【讨论】:

更不用说,闭包携带所有变量以及函数实例,所以如果你有 100 个字节的闭包和 1000 个实例,那就是 100kb 的更多内存。 @AkashKava 和@Semra,我认为这个测试并不能真正解释为什么你会在现实世界的情况下通过窗口。只有在深度嵌套的闭包沿作用域链进一步访问该变量时,才能看到性能提升。显然,如果您的函数距离变量仅在作用域链上一步之遥,您将看不到太多收益。但如果它是 waaaay 那里并且需要得到 window 你可能会看到本地副本有所收获。 @gabereal 新的 JavaScript 引擎在编译时解决了闭包问题,在运行时闭包没有性能提升。我怀疑是否有任何可衡量的性能提升,如果您有信心,您可以创建 jsperf 示例来展示性能。我们获得的唯一性能是通过隔离闭包来实现更好的缩小,从而减小大小,并且通过更快地下载和解析脚本来实现性能。 @AkashKava 是什么让你觉得我如此自信?我只是说“可能会看到一些收获”和“我不认为”。我可以创建一个测试,但我宁愿不这样做,因为我从来没有使用过这种模式。你能解释一下“在编译时解决闭包”是什么意思吗?相对于什么替代方案? Javascript 引擎之前的处理方式有何不同? 找到一个代码,其中窗口在最后被柯里化,但没有在函数的参数中输入,这是什么意思?即使我假设没有任何窗口参数,它也确保参数遵循原始全局范围对象?

以上是关于这个 JavaScript/jQuery 语法是如何工作的:(function(window, undefined) )(window)?的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript--JQuery选择器

跨浏览器窗口大小调整事件 - JavaScript / jQuery

如何在 html/javaScript/Jquery 中“浏览文件夹”

JavaScript/jQuery 中括号中的代码块是啥意思? [复制]

第五章 初始jQuery

selenium使用Xpath+CSS+JavaScript+jQuery的定位方法(治疗selenium各种定位不到,点击不了的并发症)