关于javascript:使用(函数(窗口,文档,未定义){…})(窗口,文档)赋予什么优势?

What advantages does using (function(window, document, undefined) { … })(window, document) confer?

本问题已经有最佳答案,请猛点这里访问。

我想使用这种模式是一种新的热点,但我不理解它的优势是什么,也不理解范围界定的含义。

模式:

1
2
3
4
5
6
(function(window, document, undefined){
  window.MyObject = {
    methodA: function() { ... },
    methodB: function() { ... }
  };
})(window, document)

所以我有几个问题。

封装这样的对象有什么特别的优势吗?为什么输入窗口和文档而不是正常访问?为什么要把undefined传进来?将我们正在创建的对象直接附加到窗口是一个特别好的主意吗?

我已经习惯了我称之为Javascript封装的Crockford风格(因为我从Douglas Crockford Javascript视频中获得了它)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NameSpace.MyObject = function() {
  // Private methods
  // These methods are available in the closure
  // but are not exposed outside the object we'll be returning.
  var methodA = function() { ... };

  // Public methods
  // We return an object that uses our private functions,
  // but only exposes the interface we want to be available.
  return {

    methodB: function() {
      var a = methodA();
    },
    methodC: function() { ... }

  }
// Note that we're executing the function here.
}();

这些模式中的一个在功能上比另一个好吗?第一个是另一个的进化吗?


Why are window and document being fed in instead of just being accessed normally?

一般来说,为了加快标识符解析过程,将它们作为局部变量可以有所帮助(尽管IMO的性能改进可以忽略不计)。

传递全局对象也是非浏览器环境中广泛使用的一种技术,在该环境中,全局范围内没有window标识符,例如:

1
2
3
4
(function (global) {
  //..
})(this); // this on the global execution context is
          // the global object itself

Why the heck is undefined being passed in?

之所以这样做,是因为ecmascript 3中的undefinedglobal属性是可变的,这意味着有人可以更改其影响代码的值,例如:

1
2
3
4
undefined = true; // mutable
(function (undefined) {
  alert(typeof undefined); //"undefined", the local identifier
})(); // <-- no value passed, undefined by default

如果仔细观察undefined实际上没有被传递(函数调用没有参数),这是在不使用window.undefined属性的情况下获取undefined值的可靠方法之一。

javascript中的undefined这个名字没有什么特别的意思,也不是像truefalse等关键字,只是一个标识符。

只是为了记录,在EcmaScript 5中,此属性被设置为不可写…

Is attaching the object we're creating directly to window a particularly good idea?

当您在另一个函数范围内时,这是一种常用的声明全局属性的方法。


这种风格确实比"Crockford"风格带来了一些好处。首先,传递windowdocument可以更有效地缩小脚本。小型化器可以将这些参数重命名为单字符名称,每次引用分别保存5和7个字节。可以加起来:jquery引用window33次,document91次。将每个令牌缩小到一个字符可节省802字节。

此外,您还可以获得执行速度方面的好处。当我第一次读到@joekarl关于它提供性能优势的断言时,我想,"这似乎有点假。"所以我用一个测试页来描述实际的性能。引用window一亿次,本地变量引用在firefox 3.6中提供了20%的适度速度增长(4200毫秒到3400毫秒),在chrome 9中提供了31000%的惊人增长(13秒到400毫秒)。

当然,在实践中,您永远不会引用window100000000次,即使是10000次直接引用,在chrome中也只需要1毫秒,因此实际的性能增益几乎可以忽略不计。

Why the heck is undefined being passed in?

因为(如@cms所提到的)令牌undefined实际上是未定义的。与null不同,它没有特殊的含义,您可以像其他变量名一样自由地分配给这个标识符。(然而,请注意,在ecmascript 5中不再是这样。)

Is attaching the object we're creating directly to window a particularly good idea?

window对象是全局范围,因此无论您是否明确地写"window",这就是您在某个时刻所做的。window.Namespace = {};Namespace = {};是等效的。


我认为这主要是针对需要在多个窗口上下文中运行的代码。假设您有一个复杂的应用程序,其中包含许多iframe和/或子窗口。它们都需要在myObject中运行代码,但您只需要加载一次。因此,您可以将其加载到您选择的任何窗口/帧中,但可以为每个窗口/帧创建一个myObject,其中包含对适当窗口和文档的引用。

采用未定义的参数试图防止未定义的参数可以更改:

1
2
undefined = 3;
alert(undefined);

参见CMS关于如何提高安全性的答案。


我同意你使用克罗克福德的风格。

回答你的问题

1.封装这样的对象有什么特别的优势吗?

我能看到的唯一优势是,通过创建窗口和文档局部变量而不是全局变量,您可以通过不能直接覆盖其中一个变量来获得一些额外的安全性,并且通过它们都是局部变量来获得一些性能增益。

2.为什么窗口和文档被输入而不是被正常访问?

上述地址。局部变量往往更快,但随着JIT编译的这些天,这已成为名义。

3.为什么未定义的heck被传入?

没有线索…

4.将我们直接创建的对象附加到窗口是否是一个特别好的主意?

可能不会,但我仍然坚持Crockford的模式,因为将函数附加到Window对象会通过Window对象将其暴露到全局堆栈的其余部分,而不是通过非标准命名空间将其暴露。