您是否曾经查看过jquery 1.4源代码,并注意到它是如何以以下方式封装的:
1 2 3 4 5 6
| (function( window, undefined ) {
//All the JQuery code here
...
})(window); |
我读过一篇关于javascript名称空间的文章,还有一篇叫做"一对重要的parens"的文章,所以我知道这里发生了什么。
但我以前从未见过这种特殊的语法。那埃多克斯1〔0〕在那里干什么?为什么需要通过EDOCX1[1]然后再次出现在结尾?
- 我想补充一下,保罗·爱尔兰在这段视频中谈到了这一点:paul irish.com/2010/10-things-i-learned-from-the-jquery-sourc‌&8203;e
- @伯吉,你把我的问题标记为另一个问题的副本,但我几乎在副本出现一年前就提出了这个问题。演员表应该是相反的。
- @德金泽:这不是早先被问到的问题,而是关于最高质量的答案。诚然,在这种情况下,它的脖子和脖子,但我发现CMS的答案是最好的。不过,请访问chat.stackoverflow.com/rooms/17讨论这个问题。
- 另请参见使用(功能(窗口、文档、未定义)…)(窗口、文档)会带来什么好处?
未定义是一个正态变量,可以简单地用undefined ="new value";来更改。所以jquery创建了一个真正未定义的本地"未定义"变量。
由于性能原因,窗口变量被设置为本地变量。因为当javascript查找变量时,它首先遍历局部变量,直到找到变量名。当找不到它时,javascript将进入下一个作用域等,直到它过滤掉全局变量。因此,如果将窗口变量设为本地变量,则javascript可以更快地查找它。更多信息:加速你的javascript-尼古拉斯C.扎卡斯
- 谢谢!那是一个非常有信息的视频。我认为您需要编辑您的响应以说"窗口变量是本地的"。我认为这是最好的答案。我数了数,发现在jquery源代码中,window对象至少被调用了17次。因此,将窗口移动到本地范围中一定会有显著的效果。
- @德金泽哦,是的,你是瑞特,当然是本地制造的。很高兴我能帮助你=)
- 根据这个更新后的测试jspef.com/short-scope/5,当通过jquery获得一个窗口常量时,传递"window"的速度实际上较慢,尤其对safari不利。因此,虽然"未定义"有一个用例,但我并不十分确信"窗口"在所有情况下都特别有用。
- 它对缩小代码也有积极的作用。所以"window"和"undefined"的每一个用法都可以被minifyer替换为一个较短的名称。
- @Dkinzer你有任何支持你的重要性的统计数据吗?
- 什么疯子会改变未定义的价值?
- @pukenis当你制作一个在数百万网站上使用的图书馆时,最好不要对疯子会做什么做出假设。
- 根据ecmascript5,现在未定义是不可写的,可配置的,所以在现代浏览器中它不再是一个问题,即使它不严格。你能在回答中提到这个吗?
未定义
通过声明undefined作为参数,但从不向其传递值,可以确保它始终是未定义的,因为它只是全局范围中可以被覆盖的变量。这使得a === undefined成为typeof a == 'undefined'的安全替代品,节省了一些字符。它还使代码更小型化,例如,可以将undefined缩短为u,从而节省更多的字符。
窗口
将window作为参数传递会在本地作用域中保留一个副本,这会影响性能:http://jspef.com/short-scope。到window的所有访问现在都必须在作用域链上减少一个级别。与undefined一样,本地拷贝再次允许更激进的缩小。
司扥噢特:
虽然jquery开发人员并不打算这样做,但是传入window可以使库更容易地集成到服务器端的javascript环境中,例如node.js,其中没有全局window对象。在这种情况下,只需更改一行就可以用另一行替换window对象。在jquery的情况下,可以创建一个模拟的window对象,并将其传递给HTML抓取(JSDOM之类的库可以这样做)。
- +1提到小型化,我希望jsferf能加上2——小型化的版本是(function(a,b){})(window);——a和b比window和undefined短得多。
- +我以前听说过范围链,但忘记了这个术语。谢谢!
- 它也是使用void(0)的一种替代方法,这也是出于同样的原因:void(0)是获取未定义值的一种安全方法。
- "您所说的"是指另一个问题:stackoverflow.com/questions/4945265/…
- @gnarf,谢谢你通知我问题合并了。我将编辑我的答案,使其更加合理;)
- 顺便说一句,global window.undefined只能在ecmascript3中被覆盖,escmascript5不允许这样做,现在大多数浏览器都是在ecmascript5标准下运行的。我想IE7允许覆盖未定义的全局。IE8+就可以了。
- 所以var window=不会在无窗口脚本中执行相同的技巧吗?
- 您可以将this传递到iife而不是window,以在browser和node.js环境中获得全局范围。示例:(function (root, undefined) { 'use strict'; var $ = root.jQuery; }(this));
其他人已经解释了undefined。undefined就像一个全局变量,可以重新定义为任何值。这项技术是为了防止所有未定义的检查被破坏,如果有人在某个地方写了,比如说,undefined = 10。无论变量undefined的值如何,一个从未通过的论点都保证是真实的undefined。
通过以下示例可以说明通过窗口的原因。
1 2 3 4 5 6 7
| (function() {
console.log(window);
...
...
...
var window = 10;
})(); |
控制台记录什么?window对象权的价值?错了!10?错了!它记录了undefined。javascript解释器(或jit编译器)以这种方式重写它-
1 2 3 4 5 6 7 8 9 10
| (function() {
var window; //and every other var in this function
console.log(window);
...
...
...
window = 10;
})(); |
但是,如果您得到window变量作为参数,那么就没有var,因此也没有惊喜。
我不知道jquery是否在这样做,但是如果您出于任何原因在函数的任何地方重新定义window局部变量,那么从全局范围借用它是一个好主意。
当有人决定在IE中重新定义窗口对象时,window就这样传入,我假设undefined也是这样,以防以后以某种方式重新分配。
该脚本中顶部的window只是命名参数"window",这个参数在全局window引用时更为局部,并且它将使用这个闭包中的代码。最后的window实际上是为第一个论点指定要通过什么,在这种情况下,window的当前含义……希望你在这之前没有搞砸window。
通过显示jquery中使用的最典型的情况,plugin .noConflict()处理,这可能更容易理解,因此对于大多数代码,您仍然可以使用$,即使它意味着除jQuery之外的其他内容:
1 2 3
| (function($) {
//inside here, $ == jQuery, it was passed as the first argument
})(jQuery); |
- 谢谢。这很有道理。不过,我认为答案是这个和@c.zakas所说的结合。
- @恐怕我不是C.Zakas;)
- @文森特,对不起——)
用1000000次迭代进行测试。这种本地化对性能没有影响。在1000000次迭代中甚至没有一毫秒。这根本没用。
- 更不用说,闭包携带了所有变量以及函数的实例,所以如果闭包中有100个字节,还有1000个实例,这意味着有更多的内存。
- @阿卡什卡瓦和@semra,我认为这个测试并不能解释为什么你会在现实世界中通过这个测试。只有当您有深度嵌套的闭包可以沿着作用域链进一步访问该变量时,才会看到性能的提高。显然,如果您的函数距离变量的作用域链只有一步之遥,那么您不会看到太多的收益。但如果是瓦艾在那里,需要得到window,你可能会看到一些地方副本的收益。
- @gabereal新的javascript引擎在编译时自己解析闭包,在运行时闭包没有性能提升。我怀疑是否有任何可测量的性能增益,如果您如此自信,您可以创建JSPerf示例来演示性能。我们得到的唯一性能是通过隔离闭包来实现更好的缩小,从而通过更快的脚本下载和解析来减少大小和性能。
- @阿卡什卡瓦是什么让你觉得我如此自信?我只是说"可能会看到一些收获"和"我不认为"。我可以创建一个测试,但我不想这样做,因为我从来没有使用过这个模式。你能解释一下你所说的"在编译时解析闭包"是什么意思吗?而不是什么选择?javascript引擎以前是如何做不同的事情的?
- 有一个find a code,其中window在末尾是循环的,但没有在函数的参数中输入,这意味着什么?它可以确保参数跟随原始全局的作用域对象,即使没有任何我假定的窗口参数?