2011-02-02 20 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變化,您也必須更新這個模塊,並遵循這種模式,並增加了自身對同一個對象的任何其他模塊。總之,無論是方法一還是方法二(具有依賴注入)都比其他方法「更好」,它只是一個偏好問題。這個討論可以帶來的唯一好處是你應該選擇一種方法並且保持一致