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

IT技术 javascript jquery syntax
2021-01-18 12:15:11

您是否曾深入了解jQuery 1.4源代码,并注意到它是如何以下列方式封装的:

(function( window, undefined ) {

  //All the JQuery code here 
  ...

})(window);

我读过一篇关于JavaScript 命名空间的文章和另一篇名为“一对重要的括号”的文章,所以我对这里发生的事情有所了解。

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

5个回答

undefined 是一个普通变量,可以简单地使用undefined = "new value";. 因此,jQuery 创建了一个本地“未定义”变量,该变量实际上是未定义的。

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

它对缩小代码也有积极的影响。所以每次使用“window”和“undefined”都可以被缩小器替换为更短的名称。
2021-03-15 12:15:11
根据这个更新的测试,jsperf.com/short-scope/5传递 'window' 实际上更慢,当它通过 jQuery 获取窗口常量时,尤其对 Safari 不利。因此,虽然 'undefined' 有一个用例,但我不太相信 'window' 在所有情况下都特别有用。
2021-03-21 12:15:11
@DKinzer 哦,是的,您说得对,当然它是本地制作的。很高兴能帮到你 =)
2021-03-23 12:15:11
@lukas.pukenis 当您正在制作一个在数百万网站中使用的库时,明智的做法是不要假设疯子会做什么。
2021-04-02 12:15:11
谢谢!那是一个信息量很大的视频。我认为您需要编辑您的回复以说“窗口变量是本地的”。我认为这是最好的答案。我数了一下,发现在JQuery源码中window对象至少被调用了17次。因此,将窗口移动到本地范围内肯定会有显着的效果。
2021-04-09 12:15:11

不明确的

通过声明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这样的库可以做到这一点)。

“你说什么”是指另一个问题:stackoverflow.com/questions/4945265/...
2021-03-16 12:15:11
@gnarf,感谢您通知问题已合并。我将编辑我的答案以使其更有意义;)
2021-03-23 12:15:11
+1 提到 Minification ,希望我可以为 jsperf +2 - 缩小版本是(function(a,b){})(window);-a并且bwindowand短得多undefined:)
2021-03-24 12:15:11
+1 我以前听说过作用域链,但忘记了这个词。谢谢!
2021-03-27 12:15:11
它也是使用 void(0) 的替代方法,其含义相同:void(0) 是获取未定义值的安全方法。
2021-03-27 12:15:11

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

传递窗口的原因可以用下面的例子来说明。

(function() {
   console.log(window);
   ...
   ...
   ...
   var window = 10;
})();

控制台记录什么?window对象的值吧?错误的!10?错误的!它记录undefinedJavascript 解释器(或 JIT 编译器)以这种方式重写它 -

(function() {
   var window; //and every other var in this function

   console.log(window);
   ...
   ...
   ...
   window = 10;

})();

但是,如果您将window变量作为参数,则没有 var,因此不会出现意外。

我不知道 jQuery 是否正在这样做,但是如果您window出于任何原因在函数中的任何位置重新定义局部变量,最好从全局范围借用它。

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

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

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

(function($) {
  //inside here, $ == jQuery, it was passed as the first argument
})(jQuery);
@Vincent,对此感到抱歉:-)
2021-03-18 12:15:11
@DKinzer 恐怕我不是 C. Zakas ;)
2021-03-19 12:15:11
谢谢。这很有意义。不过,我认为答案是这个和@C.Zakas 所说的结合。
2021-04-02 12:15:11

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

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