【问题标题】:Nicer way of checking JavaScript namespace检查 JavaScript 命名空间的更好方法
【发布时间】:2011-11-30 03:44:35
【问题描述】:

现在我有一个模块,它遵循一长串命名空间,例如:

TOP.middle.realModuleName = function () { /*...*/ }

我需要在一个页面上使用这个模块,我不确定这个页面是否包含了命名空间 Top.middle。所以我必须做类似的事情:

if (typeof TOP !== 'undefined' && TOP.middle && TOP.middle.realdModuleName) {
    new TOP.middle.realModuleName();
}

我认为if 语句看起来非常冗长。有人对如何为这种情况编写更好的参数检查模式有建议吗?

【问题讨论】:

  • 等一下,您是将此模块包含在外部脚本中还是自己编写?如果你包含它,如果函数的命名空间没有正确定义,你将在定义函数时立即出错。
  • 我将此模块包含在外部脚本中。
  • 那么,你会在检查命名空间之前抛出错误吗?在包含模块之前检查命名空间怎么样?
  • 当消费者没有正确使用我的模块时,我打算抛出一些错误。在检查命名空间之前,它们不会被抛出。初始化此模块时将抛出它们。例如,当调用new Top.middle.realModuleName(arg1, arg2) 时,realModuleName 内部会检查是否传递了arg2(假设需要arg2)。它arg2没有通过,抛出错误。
  • 是的,好的,但是一旦消费者包含模块,如果模块是不存在的命名空间的一部分,则会引发错误,对吗?我只是想弄清楚为什么要在调用命名空间内的方法之前验证命名空间...

标签: javascript namespaces


【解决方案1】:

有一种方法可以更快地做到这一点:

           ?.

示例:

const adventurer = {  name: 'Alice',  cat: {    name: 'Dinah'  }};
const dogName = adventurer.dog?.name;

更详细的答案:https://tc39.es/proposal-optional-chaining/#sec-scope

【讨论】:

    【解决方案2】:

    简答

    我了解到它的方式是(也适用于命名空间创建):

    // Check if the com namespace does not exist:
    if (!com) {
        var com = {};
    }
    
    // Check if your namespace does not exist:
    if (!com.myNamespace) {
        var com.myNamespace = {};
    }
    

    我学到的一些信息(到目前为止)

    根据我正在阅读的一本书,com 命名空间是商业产品的容器(我指的是网站、图书馆等)。

    命名空间最初是为了保存域而创建的。然而,今天你会在库、数据库甚至框架中找到com.*

    目前由VeriSign运营,至今已存在33年


    长答案

    首先,我们需要确保 ma​​in 命名空间存在。如果com 不存在,我们将执行此检查:

    if (!com) {
        var com = {};
    }
    

    com 是最常见的要检查的命名空间。但是,我们可以检查任何命名空间。

    我们可以跳过这部分,但如果把你的所有功能都混在一起,那真的会很烦人。

    如果com 不存在,则创建它。

    var com = {};
    

    最后,我们需要检查myNamespace 是否存在。我们将对com 使用相同的方法,但这一次,我们将在myNamespace 之前添加com.

    if (!com.myNamespace) {
        var com.myNamespace = {};
    }
    

    我们之所以在命名空间声明前添加com.,是因为com命名空间是在第二层,而myNamespace是在第三层。

    我建议您为项目添加第四级命名空间。
    这将确保:

    • 您的项目有它自己的自己的命名空间(如果使用了 myNamespace)。

    • 您的项目不会与其他项目混在一起(例如 com.projectA.other 和 com.projectA.yourProject)。

    • 您的函数不会混淆(例如,您有一个名为 write 的函数)。


    现在,你的代码

    你快到了! 会这样看:

    if (!TOP) {
        // Reinitialize your TOP Namespace here:
        var TOP = {};
    }
    
    if (!TOP.middle) {
        // Reinitialize your MIDDLE Namespace here:
        var TOP.middle = {};
    }
    
    if (typeof TOP.middle.realModuleName == "function") {
        new TOP.middle.realModuleName ();
    }
    else {
        // Redefine your function here (you never know ;) )
    }
    

    【讨论】:

      【解决方案3】:

      您可以使用 try/catch 并查找“not_defined”:

      try {
          TOP.middle.realModuleName = function () { /*...*/ };
      } catch(e) {
          if ( e.type == 'not_defined' ) {
              // exception
          }
          else {
              // throw other errors
              throw e;
          }
      }
      

      【讨论】:

      • 你的意思是 new TOP.middle.realModuleName() 在 try 块中吗?
      • 这也行,但是在不存在的命名空间中定义一个函数就足以引发错误,那为什么不同时做呢?
      【解决方案4】:

      我会尝试这样的事情,尽管如果它收到一些时髦的输入很容易出错:

      if(check("TOP.middle.realModuleName")) {
        //exists
      }
      
      //namespace checking function
      function check(ns) {
      
        var pieces = ns.split('.'),
            current = window;
      
        for(i in pieces) {    
          if(!(current = current[pieces[i]])) {
            return false;
          }
        }
      
        return true;
      }
      

      【讨论】:

      • 您可以通过设置current=window 然后删除第一个if 来缩短它。如果您不认为它太难读,您也可以将current=current[...] 赋值放在剩余的if 中。我们将函数长度减半。
      • 嗯,我正在考虑编写一个实用程序来检查。然而,目前这个页面只使用了这个命名空间链一次。我是否需要编写更多代码(比我已有的代码更多的代码)来支持这一点?
      • @davin 我不知道为什么我一开始就没有看到...感谢您的建议,我现在正在编辑
      • @Grace 我最好的建议是编写函数。如果您有自己的实用程序框架或辅助函数集合,请将其添加到其中。很多 JS 框架可能也包含了这个,所以如果你正在使用,请查看 API。
      • 你不应该回复我的;现在我发现了一些更险恶的东西:你应该 var i,而不是让它全球化:)
      【解决方案5】:

      试试这个简单的辅助函数:

      function exists(namespace) {    
         var tokens = namespace.split('.');
         return tokens.reduce(function(prev, curr) {
            return (typeof prev == "undefined") ? prev : prev[curr];
         }, window);
      }
      

      它将String 作为输入,如果对象存在则返回。你可以这样使用它:

      var module = exists("TOP.middle.realModuleName");
      

      例如:

      exists("noexist"); // returns undefined
      exists("window"); // returns DOMWindow
      exists("window.innerHeight"); // returns Number
      exists("window.innerHeight.toString"); // returns Function
      exists("window.innerHeight.noexist"); // returns undefined
      

      它也适用于计算结果为 false 的表达式:

      testNum = 0;
      testBool = false;
      testNull = null;
      
      exists("testNum"); // returns 0
      exists("testBool"); // returns false
      exists("testNull"); // returns null
      

      【讨论】:

        【解决方案6】:

        你可以这样在一行中完成所有事情:

        TOP && TOP.middle && TOP.middle.realdModuleName && new TOP.middle.realdModuleName;
        

        请注意,您的函数将立即运行。如果您想存储该功能以供以后重用,只需分配它(var module =)。如果未定义模块函数,则设置/丢弃undefined

        【讨论】:

        • 我理解你的意思,以及你为什么使用多个 &&。 IMO,使用太多 && 而不是 if 语句实际上会损害代码的可读性。
        • 恐怕我不能同意你的看法。我发现这比多行代码更容易阅读。但我知道你在寻找不那么冗长的东西。如果不是这样,那我想这只是个人喜好问题。
        • 重读你的问题后,我可以想象你所说的冗长并不是指太多的行,而是太多的 if 表达式。在这种情况下,我个人会投票支持 David 的答案,但使用一个空的 catch 块:try { new TOP.middle.realModuleName(); } catch(e) {} 当然,除非你需要在失败时做点什么。
        【解决方案7】:

        我使用一个函数来解析命名空间并在它不存在时创建它。这是函数:

        framework.namespace = function(baseName){
          var ns = baseName.split('.');
          var o = window;
          for (var i=0, l=ns.length; i < l; i++){
            o = o[ns[i]] = o[ns[i]] || {};
          }
          return o;
        };
        

        然后,在我需要使用命名空间之前,我这样做:

        framework.namespace("TOP.middle.realModuleName");
        TOP.middle.realModuleName = function(){...};
        

        【讨论】:

          【解决方案8】:

          只需将其封装在 TRY/CATCH 中?

          try {
              return new TOP.middle.blablabla();
          }
          catch(err) {
              // oh no!
          }
          
          return null;
          

          【讨论】:

          • 我更喜欢这个答案。对性能有影响吗?此外,如果模块TOP.middle.blablabla 有任何问题/错误,try...catch 是否会将错误与未定义的错误混合在一起?
          • 是的,如果构造函数中有异常,那么它将在try/catch中被捕获。这也可以看作是一件好事。至于性能,我怀疑会有明显的差异,当然你可以循环执行这 100 万次,但在现实世界中很少会发生这样的场景。
          • 实际上在我的模块blablabla 中,我故意抛出一些错误来警告这个模块的消费者错误的用法。我真的不想忽略这些错误,也不想在那个页面上做一个很长的 catch 块。
          • @Grace:我认为普遍的看法是try..catch 不会显着影响性能——除非发生异常,否则执行相同的代码就好像try..catch 不存在一样。与try 块中的代码相比,可以肯定地假设设置处理程序的开销是最小的。
          • @Matthew 鉴于这些答案,您的答案更接近我想要的,这是简短的。我会给你的投票。我也喜欢并尊重别人的回答。 :)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-03-12
          • 2020-06-20
          • 2014-02-24
          • 2011-11-13
          • 1970-01-01
          • 2014-11-13
          • 2014-07-07
          相关资源
          最近更新 更多