2010-04-26 25 views
148

你曾经在引擎盖下的JQuery的1.4源代码采取一看,发现它是如何封装在以下方式:JavaScript/JQuery语法如何工作:(函数(窗口,未定义){})(窗口)?

(function(window, undefined) { 

    //All the JQuery code here 
    ... 

})(window); 

我读过JavaScript Namespacing的文章,另一个叫“An Important Pair of Parens ,“所以我知道一些关于这里发生的事情。

但我从来没有见过这种特殊的语法。那是什么undefined在那里?为什么window需要通过,然后再次出现在最后?

+3

我想补充说,保罗爱尔兰在这个视频中谈到这件事:http://paulirish.com/2010/10-things-i-learned-from-the-jquery-source/ – Mottie 2011-02-09 13:51:32

+0

@Bergi你标记我的问题作为另一个副本,但我在重复前一年问了问题。演员应该是相反的。 – dkinzer 2014-07-16 23:33:40

+0

@dkinzer:这不是关于被问及的问题,而是关于最高质量的答案。无可否认,在这种情况下,它是脖子和脖子,但我发现CMS的答案是最好的。请参阅http://chat.stackoverflow.com/rooms/17/来讨论这个问题,尽管 – Bergi 2014-07-16 23:49:37

回答

154

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

由于性能原因,窗口变量是本地的。因为当JavaScript查找一个变量时,它首先通过局部变量,直到找到变量名称。当找不到时,JavaScript会遍历下一个范围等,直到它通过全局变量进行过滤。所以如果窗口变量是本地的,JavaScript可以更快地查找它。 更多信息:Speed Up Your JavaScript - Nicholas C. Zakas

+1

谢谢!这是一个非常丰富的视频。我认为你需要编辑你的回应,说“窗口变量是本地的”。我确实认为这是最好的答案。我统计并发现,窗口对象在JQuery源代码中至少调用了17次。所以在将窗口移动到本地范围时会产生显着的效果。 – dkinzer 2010-04-26 20:47:26

+0

@DKinzer哦,是的,你是正确的,当然它是当地的。很高兴我可以帮你=) – Vincent 2010-04-27 15:00:05

+1

根据这个更新的测试http://jsperf.com/short-scope/5传递'窗口'实际上比较慢,当它通过jQuery获得一个窗口常量时,对Safari尤其不利。所以虽然'未定义'有一个用例,但我不确定'window'在所有情况下都是特别有用的。 – hexalys 2013-04-28 10:38:09

5

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

该脚本中的顶部window只是命名参数“window”,这是一个更局部的参数,全局参数window以及此闭包内部的代码将使用哪个参数。最后的window实际上是指定为第一个参数传递什么,在这种情况下,当前的含义是window ......希望在这种情况发生之前,您还没有搞砸window

这可能更容易想到通过展示jQuery中使用的最典型的案例,插件.noConflict()处理,所以对于广大的代码,你仍然可以使用$,即使这意味着一些其他jQuery此范围之外:

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

谢谢。这很有意义。虽然,我认为答案是这个和C.Cakas说的结合。 – dkinzer 2010-04-26 20:50:16

+0

@DKinzer恐怕我不是C. Zakas;) – Vincent 2010-04-27 15:02:01

+0

@Vincent,对不起:-) – dkinzer 2010-04-27 15:14:03

15

其他已解释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变量作为自变量,有没有变种,因此没有惊喜。

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

52

未定义

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

+2

+1提到缩小,希望我能+2为jsperf - 缩小版本为'(function(a,b){})(window);' - ' a''和'b'比'window'和'undefined'短得多:) – gnarf 2011-02-09 13:25:43

+0

+1我以前听说过*范围链*,但忘记了这个词。谢谢! – alex 2011-02-09 13:25:55

+0

它也是使用void(0)的一种替代方法,它的意思是出于同样的原因:void(0)是获取未定义值的安全方法。 – glmxndr 2011-02-09 13:26:23

5

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

+2

更不用说,闭包带有函数实例的所有变量,所以如果你有100关闭中的字节数和1000个具有更多内存的100kb的实例。 – 2013-07-31 08:50:35

+0

@AkashKava和@Semra,我不认为这个测试真正解释了为什么你会在现实世界的情况下通过窗口。性能增益只有在深度嵌套闭包可以在范围链中进一步访问该变量时才能看到。显然,如果你的函数只是从变量的作用域链上离开一步,你就不会看到太多的收获。但如果它在那里等待并需要获得'''window''',你可能会看到本地副本的一些收益。 – gabereal 2014-12-02 21:42:59

+0

@ gabereal新的JavaScript引擎在编译时解决了闭包问题,运行时在闭包时没有性能增益。我怀疑是否有任何可衡量的性能增益,如果您非常自信,可以创建jsperf示例来演示性能。我们唯一的表现是通过隔离闭合来实现更好的缩小效果,从而缩小尺寸,并通过更快地下载和解析脚本来实现性能。 – 2014-12-04 10:59:49