协慌网

登录 贡献 社区

测试是否存在嵌套的 JavaScript 对象键

如果我有一个对象的引用:

var test = {};

可能(但不是立即)具有嵌套对象,例如:

{level1: {level2: {level3: "level3"}}};

检查深度嵌套对象中属性是否存在的最佳方法是什么?

alert(test.level1);产生undefined ,但是alert(test.level1.level2.level3);失败。

我目前正在做这样的事情:

if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
    alert(test.level1.level2.level3);
}

但我想知道是否有更好的方法。

答案

如果您不想要TypeError因为如果其中一个成员为nullundefined ,并且您尝试访问成员,则将引发异常。

您可以简单地catch异常,也可以创建一个函数来测试多个级别的存在,如下所示:

function checkNested(obj /*, level1, level2, ... levelN*/) {
  var args = Array.prototype.slice.call(arguments, 1);

  for (var i = 0; i < args.length; i++) {
    if (!obj || !obj.hasOwnProperty(args[i])) {
      return false;
    }
    obj = obj[args[i]];
  }
  return true;
}

var test = {level1:{level2:{level3:'level3'}} };

checkNested(test, 'level1', 'level2', 'level3'); // true
checkNested(test, 'level1', 'level2', 'foo'); // false

ES6 更新:

这是原始功能的简化版本,使用了 ES6 功能和递归功能(也采用了 正确的尾部调用形式):

function checkNested(obj, level,  ...rest) {
  if (obj === undefined) return false
  if (rest.length == 0 && obj.hasOwnProperty(level)) return true
  return checkNested(obj[level], ...rest)
}

但是,如果要获取嵌套属性的值,而不仅要检查其存在,那么这里有一个简单的单行函数:

function getNested(obj, ...args) {
  return args.reduce((obj, level) => obj && obj[level], obj)
}

const test = { level1:{ level2:{ level3:'level3'} } };
console.log(getNested(test, 'level1', 'level2', 'level3')); // 'level3'
console.log(getNested(test, 'level1', 'level2', 'level3', 'length')); // 6
console.log(getNested(test, 'level1', 'level2', 'foo')); // undefined
console.log(getNested(test, 'a', 'b')); // undefined

上面的函数允许您获取嵌套属性的值,否则将返回undefined

更新 2019-10-17:

可选的链接建议已在 ECMAScript 委员会流程的第 3 阶段到达,这将使您可以使用令牌?. ,新的可选链接运算符

const value = obj?.level1?.level2?.level3

如果访问的任何级别为nullundefined则表达式将自行undefined

该建议还允许您安全地处理方法调用:

obj?.level1?.method();

如果objobj.level1obj.level1.methodnullundefined ,则上面的表达式将产生undefined ,否则将调用该函数。

您可以使用可选的链接插件通过 Babel 开始使用此功能。

Babel 7.8.0 开始,默认情况下支持 ES2020

在 Babel REPL 上检查此示例。

🎉🎉更新:2019 年 12 月🎉🎉

可选的链接建议最终在 TC39 委员会的 2019 年 12 月会议上达到了第 4 阶段。这意味着此功能将成为ECMAScript 2020标准的一部分。

这是我从 Oliver Steele 挑选的一种模式:

var level3 = (((test || {}).level1 || {}).level2 || {}).level3;
alert( level3 );

实际上,整篇文章都是关于如何在 javascript 中执行此操作的讨论。他决定使用上面的语法(一旦习惯就不难读)作为习语。

更新

看起来 lodash 为所有嵌套属性_.get

_.get(countries, 'greece.sparta.playwright')

https://lodash.com/docs#get


先前的答案

lodash用户可能会喜欢lodash.contrib ,它有几种方法可以缓解此问题

getPath

签名: _.getPath(obj:Object, ks:String|Array)

根据给定键描述的路径获取嵌套对象中任意深度的值。键可以数组或点分隔的字符串形式给出。如果无法到达路径,则返回undefined

var countries = {
        greece: {
            athens: {
                playwright:  "Sophocles"
            }
        }
    }
};

_.getPath(countries, "greece.athens.playwright");
// => "Sophocles"

_.getPath(countries, "greece.sparta.playwright");
// => undefined

_.getPath(countries, ["greece", "athens", "playwright"]);
// => "Sophocles"

_.getPath(countries, ["greece", "sparta", "playwright"]);
// => undefined