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

JavaScript library functions in global namespace - drawbacks?

本文关键字:库函数 缺点 JavaScript 命名空间 全局      更新时间:2023-09-26

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

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

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

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

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

window.map = _.map;
window.filter = _.filter;
map(...);
filter(...);

会有什么负面影响(除了全局名称空间污染)?

我一定要期待任何性能问题吗?

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

链接仍然有效吗?

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

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

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

以下是避免将mapfilter等许多事物作为全局变量的一些原因:

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

  2. 当符号只是全局的时,代码模块就不那么自我描述了。嗯,符号map()是从哪里来的。这是内置的吗?它是本地定义的吗?它是由某个库定义的吗?知道gutils.map()来自哪里(来自gutils模块)要容易得多。大量全局性的东西比把东西分解成定义良好的模块更难维护。

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

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

如何以及为什么避免Javascript 中的全局变量

I';听说全局变量不好,我应该使用什么替代解决方案?

基本Javascript名称间隔模式

Javascript命名空间和模块

如何在JavaScript中声明命名空间?


至于性能主题,它不应该是您首先关心的问题,在大多数代码中也可能不是相关的问题。更重要的是,从一个健壮、可维护的编码策略开始,然后只优化性能至关重要的小块代码,而不是一开始就以性能的名义牺牲健壮性。

在性能至关重要的任何紧密循环中,您总是可以将任何函数分配给局部变量,以略微提高性能,这将比模块引用或全局引用更快(因为局部在全局之前解析)。

因此,如果你在一个模块中有gUtils.map(),并且你想最大限度地提高特定功能的性能,你可以这样做:

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

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

考虑这样的库,

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

如果你使用

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

库在函数2中使用"this"运算符。您的方式在此处更改了函数2的范围。function2()调用将给出一个错误,表示"function1未定义"。

您不需要担心性能;即使你在符号查找中添加了一个额外的层,大多数现代JS引擎也会将其别名。你也不应该担心增加本地对象会导致异常——这是允许的。不,最大的问题是名称空间污染,你似乎对此不屑一顾。

当您的代码在本机实现这些功能的较新浏览器上运行时会发生什么?假设你在2009年写了这段代码,并在Array.prototype中添加了.map()。如果有人添加了一个新的库,该库需要一个函数签名(原生签名),而得到了一个不同的函数签名(你的),那么你的代码现在会遇到一些大问题。

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

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

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