如何检查对象中的对象是否存在

8
似乎以下用于检查对象成员是否存在的技术会产生错误,因为在检查之前尚未声明“bar”父对象,这意味着我必须在检查之前声明它或使用两个'typeof'表达式,但这都是多余的代码。
var foo = {},
    newVal = (typeof foo.bar.myVal !== 'undefined' ? foo.bar.myVal : null );

Error: foo.bar is undefined

那么,如何检查未声明对象中的成员是否存在,而不会产生错误呢?
我喜欢JavaScript,但有时...

请将主题行中不包含标签。 - ThiefMaster
6个回答

11

可以简单地使用以下代码完成:

var newVal = (foo && foo.bar && typeof foo.bar.myVal !== 'undefined') ? foo.bar.myVal : foo.bar.myVal
如果一个属性是null或undefined,它会被视为false,因此上面的代码只处理第一个“false”语句。

4
这似乎是JavaScript的缺陷之一。我们应该能够在不必先检查父对象是否存在的情况下检查对象子元素是否存在。 - Steve
如果您确定父级始终存在,那么可以做出这样的假设。个人而言,如果我将对象作为参数传递,我总是会检查该对象是否有效。 - Dino Gambone
这一点并不简单。一旦你手动检查一个返回变量(可能来自远程主机API),就有可能导致应用程序崩溃。 - Raz

6
var newVal = ('foo' in window && // could be typeof foo !== 'undefined' if you want all scopes
             'bar' in foo &&
             'myVal' in foo.bar) ? foo.bar.myVal : null;

公正地说,这几乎读起来像自然语言。

我会在条件周围添加括号。尽管运算符优先级可能没问题,但加上括号更明显。 - ThiefMaster
@ThiefMaster,我已经添加了它们。虽然我不喜欢它们,但在我看来,这样看起来不太明显。算了,我就留着它们吧。 - davin

3
最简单的测试是:

if (foo && foo.bar) {
  // play with foo.bar.myVal  ... 
}

难道没有更好的方法吗?例如,如果您只键入if (foo),则可以轻松检查对象foo是否存在,但是如果您想要检查if (foo.a.b),浏览器不能告诉a不存在那么b也不存在吗? - Muhammad Umer
1
这很重要,因为如果我有像a.b.c.d.e这样的对象,并且我想检查e是否存在,我真的必须使用&&疯狂判断。 - Muhammad Umer
1
@MuhammadUmer——不幸的是,语言规范说明试图访问不存在对象的属性(试图访问undefined的属性)必须抛出一个错误。你可以将其用try..catch包装起来,但这不被认为是良好的实践,有性能影响,并且很可能比使用&&更多的代码。 - RobG
1
真的非常麻烦,举例来说,如果我从服务器获取 JSON 数据,它可能包含复杂的错误信息或数据。错误信息可能看起来像这样:data.client.a.error.msg,或者数据像这样:data.client.oauth.about.user.name - Muhammad Umer
你知道这种方法吗:https://dev59.com/F1jUa4cB1Zd3GeqPU9I-#56261366? - Gabrielizalo
显示剩余2条评论

2
你可以将其封装成一个方法:
function checkGraph(obj, graphPath)
{
  var parts = graphPath.split(".");
  var root = obj;

  for (var i = 0; i < parts.length; i++)
  {
    var part = parts[i];
    if (root[part] && root.hasOwnProperty(part))
      root = root[part];
    else
      return false;
  }

  return true;
}

你可以称之为:

var foo = {},
    newVal = checkGraph(foo, "bar.myVal") ? foo.bar.myVal : null;

是的,那是唯一的方法。否则,这并不简单。 - Raz

1

正如Matthew Abbott在他的回答中提到的那样,如果你需要经常深入子属性,整个对象或链中的某个位置可能为空,你可以编写一个方法以便以后使用。下面是我使用lodash实现的代码。

checkGraph(obj, graphPath) {
    if (obj) {
      let root = obj;
      _.each(graphPath.split('.'), part => {
        if (root[part]) {
          root = root[part];
        } else {
          return false; // bad property
        }
      });
      return true;
    }
    return false; // bad object
  }

您可以像这样调用该方法:

if (this.checkGraph(object, 'property.subproperty') {
    // do thing
}

1

还有另外一种方法,就是使用像Babel这样的JS转译器

if (foo?.bar) {
  // play with foo.bar.myVal  ... 
}

将可选链操作符(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining)成为未来Javascript的一部分。 - Tom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接