2011-02-02 54 views
7

查看一些JavaScript库和其他人的代码我见过两种常见模式,我不知道在使用其中一种模式时是否存在差异或优势。该模式看起来有点像这样:这两个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; 
})(); 

此图案相同或做他们中的一个具有优势或不同的使用比其他?

在此先感谢。

+4

我个人更喜欢第一个,因为它没有“硬编码”对象 – mplungjan 2011-02-02 06:38:38

回答

4

第二种假设在父范围中存在一个名为window的对象,并在其中指定一个属性。

第一个将它留给调用者进行分配,并且不依赖于定义的window(它可能仅在Web浏览器内部)。

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

+1

好吧,很少ECMAscripts(如果有的话)与浏览器和其他平台同时兼容。所以把“window”假设为全局对象并不是那么大的错误。 – jAndy 2011-02-02 06:50:04

+0

@jAndy:我不知道,我已经写了几个适用于浏览器和非浏览器环境的脚本。如果你需要全局对象,你可以在不使用`window`的情况下得到它:`var globalObj =(function(){return this;})();` – 2011-02-02 09:25:17

-2

第二种形式有一个轻微的优势,因为你有一个完全独立的功能;例如,你可以有一个标准的页眉和页脚为您的JS文件。

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

(function() { 
    // Private vars 

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

虽然打破了一点,当你做多件事情,如建立了多种方法,而不是单个对象如本例中的对象。

0

在第一个示例中,如果在另一个函数中定义了app,则app仅在该本地范围内可用,而在第二个示例中,app变量明确指定给全局范围。

在第二个示例中,app只会在全局作用域(如果在函数外部定义的情况下)被分配给全局作用域。

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.$, 

... 

但原型JS库不前:

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) 
    } 
    })(), 

... 

我不知道有任何理由一个是优于另一个,或者他们完成不同的任务(在窗口命名空间中创建应用程序对象)。

2

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


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

我偏爱第一种方法的另一个原因是,它更清晰地改变了定义模块的范围。您定义的每个模块都不需要成为全球范围的一部分。使用第二种方法,如果范围不是通过传入父对象来注入的,因为Jared Farrish以他的jQuery示例说明,那么如果决定更改该父对象的名称,则会冒着破坏代码的风险。这个例子说明了这一点:

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

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

随时标识符namespacesubns变化,您也必须更新这个模块,并遵循这种模式,并增加了自身对同一个对象的任何其他模块。总之,无论是方法一还是方法二(具有依赖注入)都比其他方法“更好”,它只是一个偏好问题。这个讨论可以带来的唯一好处是你应该选择一种方法并且保持一致