【问题标题】:What's the simplest approach to check existence of deeply-nested object property in JavaScript? [duplicate]检查 JavaScript 中是否存在深度嵌套对象属性的最简单方法是什么? [复制]
【发布时间】:2011-10-19 03:02:22
【问题描述】:

我必须检查深度嵌套的对象属性,例如 YAHOO.Foo.Bar.xyz

我目前使用的代码是

if (YAHOO && YAHOO.Foo && YAHOO.Foo.Bar && YAHOO.Foo.Bar.xyz) {
    // operate on YAHOO.Foo.Bar.xyz
}

这可行,但看起来很笨拙。

有没有更好的方法来检查这种深度嵌套的属性?

【问题讨论】:

  • 你不能只做if (YAHOO.Foo.Bar.xyz)吗?
  • @Michael,你不能只做if (YAHOO.Foo.Bar.xyz),因为如果 Foo 或 Bar 不存在,那么 if (YAHOO.Foo.Bar.xyz) 会因为取消引用未定义的中间值而引发异常。
  • 这可能是最好的检查方法;使用try...catch 会影响性能——当抛出异常时,代码会变慢。 jsPerf test.
  • @Digital Plane - 但是,如果正常情况是它存在,那么 try/catch 比多重 if 测试(在 Chrome 中)快 19 倍。

标签: javascript properties


【解决方案1】:

如果您希望YAHOO.Foo.Bar 是一个有效的对象,但又想让您的代码防弹以防万一它不是,那么在它周围放置一个 try catch 并让一个错误处理程序捕获任何可能是最干净的缺失的部分。然后,您可以只使用一个 if 条件而不是四个来检测终端属性是否存在,如果中间对象不存在,则使用一个 catch 处理程序来捕获事物:

try {
    if (YAHOO.Foo.Bar.xyz) {
        // operate on YAHOO.Foo.Bar.xyz
} catch(e) {
    // handle error here
}

或者,根据您的代码的工作方式,它甚至可能只是这样:

try {
    // operate on YAHOO.Foo.Bar.xyz
} catch(e) {
    // do whatever you want to do when YAHOO.Foo.Bar.xyz doesn't exist
}

在处理应该是特定格式的外部输入时,我特别使用这些,但无效输入是我想要捕获和处理自己的可能性,而不是仅仅让异常向上传播。

一般来说,一些 javascript 开发人员未充分使用 try/catch。我发现有时我可以用围绕较大功能块的单个 try/catch 替换 5-10 if 语句检查输入,同时使代码更简单且更具可读性。显然,何时合适取决于特定的代码,但绝对值得考虑。

仅供参考,如果通常的操作是不使用 try/catch 抛出异常,它也可以比一堆 if 语句快得多。


如果您不想使用异常处理程序,您可以创建一个函数来为您测试任意路径:

function checkPath(base, path) {
    var current = base;
    var components = path.split(".");
    for (var i = 0; i < components.length; i++) {
        if ((typeof current !== "object") || (!current.hasOwnProperty(components[i]))) {
            return false;
        }
        current = current[components[i]];
    }
    return true;
}

示例用法:

var a = {b: {c: {d: 5}}};
if (checkPath(a, "b.c.d")) {
    // a.b.c.d exists and can be safely accessed
}

【讨论】:

  • 不要这样滥用try catch。亲爱的上帝,我的眼睛。甚至是一个空的 catch 块。如果我见过的话,这是一种代码味道。
  • @Raynos - 为什么这会滥用 try/catch?对我来说,检查所有中间级别比使用多个嵌套 if 语句更有意义。一个空的捕获只是意味着如果中间级别不存在,你故意不做任何事情(一个合理的设计选择)。这是非常好的代码。您是否真的因为喜欢不同的方法而投反对票?像这样使用 try/catch 绝对没有错。
  • “异常的发生,改变程序正常执行流程的特殊情况。”我发现声称不存在的属性是“改变程序执行正常流程的特殊条件”是一个巨大的延伸。我认为您使用了错误的工具来完成这项工作。
  • 当您通常期望它存在时不存在的中间属性很可能是“改变程序正常执行流程的特殊条件”,并且似乎是异常的完美用途。您似乎认为异常应该只用于特殊的事情,但它们只是另一种错误返回/传播机制,在许多情况下可以比多个if 语句更有效地使用,特别是当您处理外国您不一定要控制的输入,并且测试每个操作可能会非常昂贵。
  • 在处理应该遵守特定格式但可能有错误的外来 HTML(不受我控制的 HTML)时,我一直使用异常。当一开始有一堆家务(解析结构,获取数据等),然后在最后应用最终结果时,它特别有用。我用 try/catch 包围整个块,如果任何结构不符合预期,它会引发错误,并且我非常有效地在一个地方捕获所有错误,而不是使用数十个 if 语句。也使代码更具可读性。
【解决方案2】:

coffeescript(编译成javascript)很好地解决了这个问题:

if YAHOO.Foo?.Bar?.xyz
  // operate on YAHOO.Foo.Bar.xyz

【讨论】:

    【解决方案3】:

    考虑这个效用函数:

    function defined(ref, strNames) {
        var name;
        var arrNames = strNames.split('.');
    
        while (name = arrNames.shift()) {        
            if (!ref.hasOwnProperty(name)) return false;
            ref = ref[name];
        } 
    
        return true;
    }
    

    用法:

    if (defined(YAHOO, 'Foo.Bar.xyz')) {
        // operate on YAHOO.Foo.Bar.xyz
    }
    

    现场演示: http://jsfiddle.net/DWefK/5/

    【讨论】:

    • 我认为您需要在 while 循环测试中明确检查 undefined,而不仅仅是真实性测试。此刻定义({a:''},'a.b')=== true。不错的方法,我喜欢它!
    • @codebox 我对我的代码不满意,所以我重写了。你怎么看?
    • 我希望在下划线或 lodash 中有这样的内容
    • 哦 lodash 确实有。 _.get(),太棒了!甚至还有一个默认参数。
    • 这是一个非常好的解决方案。但是如果引用对象未定义,代码就会中断。我认为检查是否定义了引用对象将解决问题。 Your solution, Updated one
    【解决方案4】:
    var _ = {};
    
    var x = ((YAHOO.Foo || _).Bar || _).xyz;
    

    【讨论】:

    • 为本周最不清楚的一行点赞。
    【解决方案5】:

    我实际上投票结束了作为javascript convert dotnotation string into objects重复的问题。

    但是,我想这是一个不同的主题,但如果您不想一直try-catch,那么那里的答案可能仍然有用。

    【讨论】:

      【解决方案6】:

      如果您需要检查路径的正确性,而不是“YAHOO.Foo.Bar”对象上是否存在“xyz”成员,将调用包装在 try catch 中可能是最简单的:

      var xyz;
      try {
          xyz = YAHOO.Foo.Bar.xyz;
      } catch (e) {
          // fail;
      };
      

      或者,你可以做一些 string-kong-fu-magicTM

      function checkExists (key, obj) {
          obj = obj || window;
          key = key.split(".");
      
          if (typeof obj !== "object") {
              return false;
          }
      
          while (key.length && (obj = obj[key.shift()]) && typeof obj == "object" && obj !== null) ;
      
          return (!key.length && typeof obj !== "undefined");
      }
      

      用法如下:

      if (checkExists("YAHOO.Foo.Bar.xyz")) {
          // Woo!
      };
      

      【讨论】:

      • 你什么时候会使用 checkExists 函数中涉及的所有东西而不是 try/catch 方法?
      • 我只是不明白typeof obj !== "object" 测试的意义。为什么要限制?
      • @jfriend00:字符串方法可以让您找到事情发生故障的点(如果您也想这样做的话)。在某些情况下,通过字符串传递对象描述也是有意义的,例如自定义模块系统。
      • @missingno:它检查用户提供的“obj”(这是可选的;如果没有提供,它默认为window),实际上是一个对象(它阻止函数中断如果用户传递了错误的参数,基本上!)
      • @jfriend00:try/catch 如果您有很多地方要检查对象路径是否存在,则有点需要输入。
      【解决方案7】:

      使用try catch

      a={
        b:{}
      };
      
      //a.b.c.d?true:false; Errors and stops the program.
      
      try{
        a.b.c.d;
      }
      catch(e){
        console.log(e);//Log the error
        console.log(a.b);//This will run
      }
      

      【讨论】:

        猜你喜欢
        • 2012-04-02
        • 1970-01-01
        • 2010-11-19
        • 2020-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-12
        相关资源
        最近更新 更多