全局命名空间中的JavaScript库函数 - 缺点?

时间:2014-11-02 18:04:04

标签: functional-programming global-variables underscore.js javascript

像Underscore.js和LoDash.js这样的JS库通常通过全局对象_提供函数:

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

这些基本函数是Python中的全局函数,也是标准库的一部分:

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

如果我将库函数提升为全局函数......

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

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

......会有什么缺点(全局命名空间污染除外)?

我是否必须期待任何性能问题?

是否会导致某些浏览器出错? (例如,如果内部JS函数被库函数覆盖,而库函数又依赖于本机函数)

链接还能用吗?

4 个答案:

答案 0 :(得分:2)

有许多缺点,但性能不太可能是其中之一。

如果两个库具有map()功能并且行为略有不同怎么办?如果他们都将map()放在全球空间中,您将无法访问其中一个。而且您可能无法轻易知道您实际想要的map()已被覆盖。

顺便说一句,map()filter()是最近的JS实现的一部分,但即使在那里,它们也是使用它们的对象原型的一部分。所以你有Array.prototype.map()Array.prototype.filter()

答案 1 :(得分:2)

以下是避免将mapfilter等大量内容变为全局变量的一些原因:

  1. 命名与尝试执行类似但不兼容的其他代码/库的冲突。如果所有代码都使用了大量的全局函数,那么单个全局函数的简单定义可能会完全破坏你的应用程序,因为它将重新定义用于代码中其他地方的其他东西的函数。这本身就是使用尽可能少的全局变量的原因。在使用重要的第三方库的团队项目或项目中,这是不可能管理的,并且将不可避免地导致生产力损失,或者甚至更糟糕的是,可能不会立即出现的坏错误。

  2. 当符号只是全局时,代码模块不太自我描述。嗯,符号map()来自何处。这是内置的吗?它是在本地定义的吗?它是由某个图书馆定义的吗?知道gutils.map()来自哪里(来自gutils模块)要容易得多。大量的全局事物比将事物分解为明确定义的模块更难以维护。

  3. 将函数定义为对其运行的对象的方法有很多好处(例如ES5 .map().filter()是Array对象上的方法而不是通用的全局变量。现在,将非标准方法添加到现有内置对象(也是为了命名冲突原因)被认为是不好的做法,但鼓励添加实现标准行为的polyfill。

  4. 有关其他信息,请参阅这些参考资料:

    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?


    关于性能主题,它不应该是您的第一个问题,在大多数代码中甚至可能不是相关问题。更重要的是从一个强大的,可维护的编码策略开始,然后只优化性能至关重要的小部分代码,而不是在性能名称的开头牺牲稳健性。

    在性能严格的任何紧密循环中,您始终可以将任何函数分配给局部变量以略微提高性能,这将比模块引用或全局引用更快(因为本地化在全局变量之前被解析)。 / p>

    因此,如果您在模块中有gUtils.map()并希望在特定函数中最大化性能,则可以执行以下操作:

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

答案 2 :(得分:1)

您无需担心性能问题;即使您在符号查找中添加了额外的图层,大多数现代JS引擎也会对其进行别名。您也不应该担心增加导致异常的本机对象 - 这是允许的。不,最大的问题是命名空间污染,你似乎对它有点不屑一顾。

当您的代码在本地实现这些功能的较新浏览器上运行时会发生什么?假设您在2009年编写了此代码,并将.map()添加到Array.prototype。如果有人添加了一个期望一个函数签名(本地签名)并且得到另一个(你的)的新库,那么你的代码现在会有一些大问题。

如果必须这样做,至少要检查全局符号是否已存在,例如

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

此外,请确保依赖于您的地图实现的任何代码都可以将其依赖关系发送到JavaScript依赖关系管理工具(如RequireJS),或者您已经拥有一个强大的DM系统,并且您的本机覆盖在任何之前都已附加其他代码执行(例如,不要将代码放在异步脚本标记中,不要将执行推迟到DOMContentLoaded等)。

答案 3 :(得分:1)

考虑像这样的库,

_ = (function(){

   lib = {};

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

   //more code

   return lib;

})();

如果你使用,

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

图书馆使用&#34;这个&#34; function2中的运算符。你的方式在这里改变了function2的范围。 function2()调用将给出错误说&#34; function1未定义&#34;。