0

像Underscore.js和LoDash.js JS庫提供的功能,通常是通過一個全局對象_全局命名空間中的JavaScript庫函數 - 缺點?

_.map(...); 
_.filter(...); 

這種基本功能是在Python全局函數和標準庫的一部分:

map(...) 
filter(...) 

如果我將圖書館功能提升爲全球功能...

window.map = _.map; 
window.filter = _.filter; 

map(...); 
filter(...); 

...會有什麼缺點? (除全局命名空間污染)?

我是否期待任何性能問題?

它會導致某些瀏覽器出錯嗎? (例如,如果內置的JS函數被庫函數覆蓋,而庫函數又依賴於本機函數)

鏈接是否仍然有效?

+0

爲什麼選擇全球?如果* *你明確地導入它們,那麼將函數導入到局部變量中。 – Bergi 2014-11-02 18:24:39

+0

我發現它更方便,而不必爲基本函數輸入'_.',尤其是在我的Python背景下(加上我是代碼美學)。人們也可以反過來爭辯,爲什麼要擁有全球功能。像alert()這樣的函數應該只能通過'window.alert()'等等。一個很好的答案,爲什麼你想要某些東西是全球性的/短的在這個談話中給出:https://www.youtube.com/watch?v=o9pEzgHorH0 – CoDEmanX 2014-11-02 18:48:54

+0

是的,省略'_.'並指定'map'等功能*變量*。但是沒有理由讓這些變量成爲全球變量。讓它們對你的模塊是本地的 - 到你控制的代碼段。你不會導入一個模塊,並將其方法分配給python中的全局變量。 – Bergi 2014-11-02 20:21:38

回答

1

考慮這樣一個圖書館,

_ = (function(){ 

    lib = {}; 

    lib.function1 = function(){ 
     //code 
    }; 
    lib.function2 = function(){ 
    this.function1(); 
    }; 

    //more code 

    return lib; 

})(); 

如果您使用,

window.function2 = _.function2; 
function2(); 

該庫在函數2中使用「this」運算符。你的方式在這裏改變了function2的範圍。函數2()調用會給出錯誤,說「函數1未定義」。

+0

好點,所以我將不得不改變整個庫來使用全局變量,不是嗎? – CoDEmanX 2014-11-02 18:52:10

+0

您可以使用「call()」方法或「apply()」方法。例如,在這裏您可以使用function2.call(_)或function2.apply(_),而不是在最後一行使用function2()。 – 2014-11-02 19:06:49

+0

或者你可以使用「bind()」方法和「window.function2 = _.function2.bind(_)」。那麼在「function2()」調用中沒有問題。 – 2014-11-03 09:40:38

2

存在多種缺點,但性能不可能是其中之一。

如果兩個函數庫具有map()函數並且行爲稍有不同,該怎麼辦?如果他們都把他們的map()放在全球空間中,您將無法訪問其中的一個。而且您可能無法輕鬆知道您實際需要的map()已被覆蓋。

順便說一句,map()filter()是最近ish JS實現的一部分,但即使在那裏,它們也是使用它們的對象的原型的一部分。所以你有Array.prototype.map()Array.prototype.filter()

+0

如果您嘗試使用下劃線和lodash,它們也會覆蓋'_',除非您確實如此。 'lodash = _.noConflict();'。當然,我只會把一個圖書館的功能推廣到全球功能,所以我不認爲這是一個問題。在對象上使用原型函數很好,但是它與面向對象的函數編程風格相反。我不希望使用'Array.prototype.map(...);'來調用它,它會打破使函數成爲全局的目的(=輸入更少,更清晰的可視代碼)。 – CoDEmanX 2014-11-02 18:34:43

1

您不需要擔心性能;即使您爲符號查找添加了額外的圖層,大多數現代JS引擎也會將其替換。你也不應該擔心增加導致異常的本地對象 - 這是允許的。不,最大的問題是命名空間污染,你似乎有點不屑一顧。

當您的代碼在本地實現這些功能的新瀏覽器上運行時會發生什麼?假設您在2009年編寫了此代碼,並將.map()添加到Array.prototype。如果有人添加了一個期望一個函數簽名(原生函數簽名)並獲得另一個函數簽名(您的)的新庫,那麼現在您的代碼會出現一些大問題。

如果你必須這樣做,至少檢查全局符號是否已經存在,例如,

window.map = window.map || myLib.map; 

同時,確保依靠地圖實現任何代碼可以通知其依賴於像RequireJS一個JavaScript依賴管理工具,或者你有一個強大的DM系統已經到位,並確保您的本地覆蓋在任何其他代碼執行之前附加(例如,不要將代碼放在異步腳本標記中,不要推遲執行,直到DOMContentLoaded等等)

+0

Python中有很多全局函數,因此你不能將自己的名字用於你自己的變量,並且我從來沒有遇到任何問題(所以是的,我並不介意命名空間的污染)。如果lib函數的簽名更改爲遵循新的標準,那麼我將不得不調整我的代碼,或者使用舊版本的庫。這似乎不是在全局名稱空間中使用庫函數(並使它們覆蓋內置函數)的特殊問題。我不知道鏈接是否能正常工作... – CoDEmanX 2014-11-02 18:42:43

2

這裏有一些理由來避免很多的東西像mapfilter是全局變量:

  1. 與其他代碼命名衝突/庫試圖做類似,但不兼容的東西。如果所有代碼都使用多個全局函數,那麼單個全局函數的簡單定義可能會徹底破壞您的應用程序,因爲它將重新定義用於代碼中其他位置的其他位置的此類函數。這本身就是使用盡可能少的全局變量的原因。在一個團隊項目或使用重要第三方庫的項目中,這是不可能管理的,並且不可避免地會導致生產力損失,甚至更糟糕的是可能不會立即顯現的錯誤錯誤。

  2. 當一個符號是全局的時候,代碼模塊不那麼自我描述。嗯,符號map()來自哪裏。這是內置的嗎?它是在本地定義的嗎?它是由一些圖書館定義的嗎?知道gutils.map()來自哪裏(來自gutils模塊)要容易得多。大量的全球任何東西都不如將事物劃分成定義明確的模塊更容易維護。

  3. 有許多優點,以限定功能上其運行的對象(如ES5 .map().filter() Array對象上是方法,而不是如普通的全局)的方法。現在,將非標準方法添加到現有內置對象(也用於命名衝突原因)被認爲是不好的做法,但鼓勵添加實現標準行爲的polyfill。

查看其他信息這些引用:

How and Why to Avoid Globals in Javascript

I've Heard Global Variables Are Bad, What Alternative Solution Should I Use?

Essential Javascript Namespacing Patterns

Javascript Namespaces and Modules

How do I declare a namespace in JavaScript?


至於性能的話題,它不應該是你首先關注的,並很可能根本就不在大多數代碼相關的問題。從一個強大的,可維護的編碼策略開始,然後只優化性能至關重要的小部分代碼,而不是犧牲性能名稱的開始時的穩健性就顯得尤爲重要。

在性能至關重要的任何緊密循環中,您始終可以將任何函數分配給局部變量以略微提高性能,並且這會比模塊引用或全局引用(因爲局部變量在全局變量之前解析)更快。

所以,如果你有一個模塊中gUtils.map()並且要最大限度地提高特定函數中的表現,你可以這樣做:

function x() { 
    var map = gUtils.map; 


    for (var i = 0; i < someBigNumber; i++) { 
     map(...); 
    } 
}