这两种JavaScript模式之间有什么区别吗?

时间:2011-02-02 06:34:15

标签: javascript design-patterns

看一些JavaScript库和其他人的代码,我看到了两种常见的模式,我不知道使用其中一种模式是否存在差异或优势。模式看起来像这样:

1

var app = (function () {
    // Private vars

    // Module
    var obj = {
        prop: "",
        method: function () {}
    };

    return obj;
})();

2

(function () {
    // Private vars

    // Module
    var obj = {
        prop: "",
        method: function () {}
    };

    window.app = obj;
})();

这些模式是相同的还是其中一种比另一种更有优势或用途不同?

提前致谢。

5 个答案:

答案 0 :(得分:5)

第二个假设在父作用域中存在一个名为window的对象,并在那里指定一个属性。

第一个将它留给调用者进行赋值,并且不依赖于被定义的window(它可能只在Web浏览器中)。

所以,我会说第一个肯定更好(更独立,更少依赖环境)。

答案 1 :(得分:2)

tl; dr:选择一种方法并保持一致


在我看来,第一种方法在可读性方面略有优势。在我的脑海中,当我阅读它时,我看到,“模块app正在被定义,”并且此闭包内的所有内容都属于该模块。这对我来说是一种自然的分解,并强加了即将定义的模块的面向对象特性。

我赞成第一种方法的另一个原因是更改模块定义的范围更清晰。您定义的每个模块都不需要成为全局范围的一部分。使用第二种方法,如果未通过传递父对象注入作用域Jared Farrish用他的jQuery示例说明,那么如果您决定更改该父对象的名称,则存在破坏代码的风险。这个例子说明了这一点:

var namespace = {
  subns: { ... }
};

(function() {
  var module = { ... };
  namespace.subns.someModule = module;
}());

只要标识符namespacesubns发生更改,您还必须更新此模块以及遵循此模式的任何其他模块,并将其自身添加到同一对象。

总而言之,方法一和方法二(依赖注入)都不比另一方“更好”,这只是一个偏好问题。这次讨论的唯一好处是你应该选择一种方法并保持一致

答案 2 :(得分:1)

他们都在完成同样的事情,在运行代码时在全局命名空间中创建一个对象。

其中一个并不比另一个更“硬编码”,因为它们都没有进行任何类型的函数原型设计,您可以使用new关键字创建对象的克隆。在我看来,这只是一个偏好问题。

例如,jquery做了类似于后者的事情:

(function( window, undefined ) {

// Use the correct document accordingly with window argument (sandbox)
var document = window.document;
var jQuery = (function() {

// Define a local copy of jQuery
var jQuery = function( selector, context ) {
        // The jQuery object is actually just the init constructor 'enhanced'
        return new jQuery.fn.init( selector, context, rootjQuery );
    },

    // Map over jQuery in case of overwrite
    _jQuery = window.jQuery,

    // Map over the $ in case of overwrite
    _$ = window.$,

...

但Prototype JS Library是前者:

var Prototype = {
  Version: '1.6.1',

  Browser: (function(){
    var ua = navigator.userAgent;
    var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
    return {
      IE:             !!window.attachEvent && !isOpera,
      Opera:          isOpera,
      WebKit:         ua.indexOf('AppleWebKit/') > -1,
      Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
      MobileSafari:   /Apple.*Mobile.*Safari/.test(ua)
    }
  })(),

...

我不知道为什么一个比另一个更好,或者他们以不同方式完成任务(在窗口命名空间中创建app对象)。

答案 3 :(得分:0)

在第一个示例中,如果app在另一个函数中定义,app仅在该局部范围内可用,而在第二个示例中,app变量显式分配给全球范围。

在第二个示例中,app仅在函数外部的全局范围中定义时才会分配给全局范围。

答案 4 :(得分:-2)

第二种形式有一个小优势,因为你有一个完全自包含的功能;例如,您可以为JS文件设置标准页眉和页脚。

我没有完全售出的部分是块内的局部变量。我倾向于这样:

(function () {
   // Private vars

   // Module
   window.app = {
       prop: "",
       method: function () {}
   };
})();

虽然当你做的不止一件事情时会出现故障,例如用多个方法而不是像这个例子中的单个对象构建一个对象。