使用(函数(窗口,文档,未定义){...})(窗口,文档)赋予什么优势?

时间:2011-02-16 18:21:58

标签: javascript

我想使用这种模式是新的热点,但我不明白它的优点是什么,我不理解范围的含义。

模式:

(function(window, document, undefined){
  window.MyObject = {
    methodA: function() { ... },
    methodB: function() { ... }
  };
})(window, document)

所以我对此有几个问题。

封装像这样的物体有什么特别的好处吗? 为什么窗口文档被送入而不是正常访问?
为什么要传递undefined? 将我们正在创建的对象直接附加到窗口是一个特别好的主意吗?

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

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.
}();

其中一种模式在功能上优于另一种吗?第一个是另一个的演变吗?

4 个答案:

答案 0 :(得分:50)

  

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

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

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

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

为什么传递未定义heck?

这是因为ECMAScript 3中的undefined全局属性是可变的,这意味着某人可以更改其影响代码的值,例如:

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

如果仔细观察undefined实际上没有被传递(函数调用没有参数),这是获取undefined值的可靠方法之一,而不使用属性{{1 }}

JavaScript中的window.undefined名称并不意味着什么特别,不是undefinedtrue等关键字......,它只是一个标识符。

仅仅为了记录,在ECMAScript 5中,这个属性是不可写的......

  

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

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

答案 1 :(得分:26)

这种特殊风格确实带来了“Crockford”风格的一些优点。首先,通过windowdocument可以更有效地缩小脚本。 minifier可以将这些参数重命名为单字符名称,每个引用分别保存5和7个字节。这可以加起来:jQuery引用window 33次,document 91次。将每个标记缩小为一个字符可以节省802个字节。

此外,您执行可获得执行速度优势。当我第一次阅读@joekarl关于它提供性能优势的断言时,我想,“这看起来相当虚假。”所以我用test page分析了实际的表现。引用window 一亿次次,本地变量引用在Firefox 3.6中提供适度的20%速度增加(4200 ms到3400ms),并且增加了31,000%(13秒到400毫秒)在Chrome 9中。

当然,在实践中你永远不会引用window 100,000,000次,甚至10,000个直接引用只需要1ms的Chrome,所以这里实际的性能提升几乎可以忽略不计。

  

为什么要传递undefined

因为(如@CMS所述)token undefined is actually undefined。与null不同,它没有特殊含义,您可以像任何其他变量名一样自由地分配给此标识符。 (但请注意,这是no longer true in ECMAScript 5。)

  

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

window对象是全局范围,所以这正是你在某个时刻所做的,无论你是否明确地写了“窗口”。 window.Namespace = {};Namespace = {};相同。

答案 2 :(得分:1)

我和你一起使用Crockford的风格。

回答你的问题

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

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

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

上面解释。局部变量往往更快,但随着jit编译,这些日子变得名义上。

3.为什么未定义传入?

没有头绪......

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

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

答案 3 :(得分:1)

我认为这主要是针对需要在多个窗口上下文中运行的代码。假设您有一个包含大量iframe和/或子窗口的复杂应用程序。它们都需要在MyObject中运行代码,但您只想加载一次。因此,您可以在您选择的任何窗口/框架中加载它,但是您可以为每个窗口/框架创建一个MyObject,并引用正确的窗口和文档。

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

undefined = 3;
alert(undefined);

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