【问题标题】:Are there any dangers associated with using JavaScript namespaces?使用 JavaScript 命名空间是否有任何危险?
【发布时间】:2012-02-13 13:20:16
【问题描述】:

在创建 JavaScript 命名空间时应注意哪些危险/注意事项?

我们的项目相当广泛,我们正在运行大量 JavaScript 文件(20 多个,期待更多)。不使用命名空间就不可能有任何代码可维护性,所以我们这样实现它们:

var namespace1 = {

  doSomething: function() {
    ...
  },

  doSomethingElse: function() {
    ...
  }

}

然后为了创建层次结构,我们像这样链接它们:

var globalNamespace = {
  functions1: namespace1,
  functions2: namespace2,
  ...

}

这很好用,但它本质上是一个“技巧”,让 JS 表现得好像它确实有命名空间一样。尽管这种方法被大量使用,但大多数关于此的文献似乎都集中在如何做到这一点上,而不是是否有任何可能的缺点。随着我们编写更多的 JS 代码,这正迅速成为我们系统工作方式中不可或缺的一部分。因此,无缝运行非常重要。

在任何情况下,这种“诱导”命名空间系统会导致您出错,或者需要特别注意吗?我们可以安全地期望所有浏览器都有相同的行为吗?

【问题讨论】:

  • 命名空间通常不能解决冲突问题,它们只是将其向上移动一层。现在你被困在没有其他人能够在他们不相关的项目中使用namespace1 的情况下。
  • 它基于非常基本的 JavaScript 语义。没有什么危险的,甚至没有什么特别“聪明”的。在我看来,这也是收集和公开功能的一种相当薄弱的方式。
  • 唯一真正的“陷阱”是您是否计划引入“私有”变量。
  • @MarcB:这不是什么大问题。我不希望每次都加载 all 代码。主要是关于可维护性。

标签: javascript jquery version-control namespaces project-management


【解决方案1】:

由于您基本上是在向对象添加函数并将这些对象添加到其他对象中,因此我希望每个浏览器都以相同的方式处理。

但是如果你想要模块化,为什么不使用像require.js 这样的(相对)简单的框架呢?这将允许您和您的团队以模块化方式编写代码,并允许团队在需要时“导入”这些模块:

require(["helper/util"], function() {
  //This function is called when scripts/helper/util.js is loaded.
});

Require.js 会处理依赖关系,它还会防止污染全局命名空间。

【讨论】:

  • 是的,我们正在研究使用 require.js :)
  • 您的第一个想法是什么?您认为它会很容易采用,还是需要重构大量代码?
  • 应该比较容易,因为我们的代码已经非常模块化了。
  • +1 强烈同意将 RequireJS 用于依赖项。从好的方面来说,您可以避免必须定义命名空间,因为所有内容都已经在它自己的模块中定义,因此它是自己的命名空间。然后使用 require 将导入的函数绑定到一个变量。当然,您仍然可以选择使用命名空间并且不让您的模块返回任何内容。
【解决方案2】:

您在示例中定义命名空间的方式似乎是从每个命名空间中创建全局变量,因此您最终会得到

window.namespace1
window.namespace2
window.globalNamespace
window.globalNamespace.namespace1
window.globalNamespace.namespace2

因此,如果您有任何破坏 window.namespace1 的东西,它也会破坏 window.globalNamespace.namespace1

编辑:

我们是这样解决这个问题的:

namespacing = {
    init: function(namespace) {
        var spaces = []; 
        namespace.split('.').each(function(space) {
            var curSpace = window,
                i;  

            spaces.push(space);
            for (i = 0; i < spaces.length; i++) {
                if (typeof curSpace[spaces[i]] === 'undefined') {
                    curSpace[spaces[i]] = {}; 
                }   
                curSpace = curSpace[spaces[i]];
            }   
        });
    }
};

然后你这样使用它:

namespacing.init('globalNamespace.namespace1');

globalNamespace.namespace1.doSomething = function() { ... };

这样您就不必引入新的全局变量,并且可以自信地添加到现有命名空间,而不会破坏其中的其他对象。

【讨论】:

  • 是的,我们都知道。我们有编码指南来防止这种情况发生。我不想要同名的组件,即使它们不会相互冲突。
  • 我可能是错的,但我相信在这个例子中这不会是一个问题。对象是通过引用传递的,并且 namespace1 是一个对象,所以只要它在定义和赋值之间没有被“破坏”,那么你应该没问题。
  • @Goro,您的编码指南对其他人、第 3 方供应商等而言意义不大。
  • @Asmor:是的,但我们不打算让其他人直接与我们的代码交互(我们会有一个 API)
  • @goro 我已经用我们如何解决全局命名空间中有太多对象的问题的方法更新了我的答案。
【解决方案3】:

我们在工作中使用类似的系统,它可以很好地完成工作。我看不出有什么缺点。它只是对象和属性。出于同样的原因,跨浏览器兼容性应该很好。您最终可能不得不编写一些长名称来解析特定函数,例如 Foo.Bar.Test.Namespace2.Function,但即便如此,也可以通过事先将其分配给变量来解决。

【讨论】:

    【解决方案4】:

    我建议这样做,这样您就可以完全远离全局范围,除了“基本”命名空间。我们在我工作的地方做类似的事情。假设您为 Acme 公司工作,并希望 ACME 成为您的基本命名空间。

    在每个文件的顶部,您应该包括:

    if (!window.ACME) { window.ACME = {} }
    

    然后你就去定义你想要的任何东西。

    ACME.Foo = {
      bar: function () { console.log("baz"); }
    }
    

    如果你想要更深层次的命名空间,你只需对每个层次做同样的事情。

    if (!window.ACME) { window.ACME = {} }
    if (!ACME.Foo) { ACME.Foo = {} }
    

    这样每个文件都可以独立测试,它们会自动设置命名空间基础结构,但是当您将它们编译在一起或同时测试多个文件时,它们不会继续覆盖已经定义的内容。

    【讨论】:

      猜你喜欢
      • 2012-04-24
      • 2012-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-28
      • 2011-07-13
      • 2012-01-24
      相关资源
      最近更新 更多