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

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

var test = {};

that will potentially (but not immediately) have nested objects, something like:

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

What is the best way to check for the existence of property in deeply nested objects?

alert(test.level1); yields undefined, but alert(test.level1.level2.level3); fails.

I’m currently doing something like this:

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

but I was wondering if there’s a better way.

老丝村村Stafan2020/03/11 15:29:50

我编写了自己的函数,该函数采用了所需的路径,并且具有好坏的回调函数。

function checkForPathInObject(object, path, callbackGood, callbackBad){
    var pathParts = path.split(".");
    var currentObjectPath = object;

    // Test every step to see if it exists in object
    for(var i=0; i<(pathParts.length); i++){
        var currentPathPart = pathParts[i];
        if(!currentObjectPath.hasOwnProperty(pathParts[i])){
            if(callbackBad){
                callbackBad();
            }
            return false;
        } else {
            currentObjectPath = currentObjectPath[pathParts[i]];
        }
    }

    // call full path in callback
    callbackGood();
}

用法:

var testObject = {
    level1:{
        level2:{
            level3:{
            }
        }
    }
};


checkForPathInObject(testObject, "level1.level2.level3", function(){alert("good!")}, function(){alert("bad!")}); // good

checkForPathInObject(testObject, "level1.level2.level3.levelNotThere", function(){alert("good!")}, function(){alert("bad!")}); //bad
Sam小哥理查德2020/03/11 15:29:50

您可以通过使用递归函数来做到这一点。即使您不知道所有嵌套对象键的名称,此方法也将起作用。

function FetchKeys(obj) {
    let objKeys = [];
    let keyValues = Object.entries(obj);
    for (let i in keyValues) {
        objKeys.push(keyValues[i][0]);
        if (typeof keyValues[i][1] == "object") {
            var keys = FetchKeys(keyValues[i][1])
            objKeys = objKeys.concat(keys);
        }
    }
    return objKeys;
}

let test = { level1: { level2: { level3: "level3" } } };
let keyToCheck = "level2";
let keys = FetchKeys(test); //Will return an array of Keys

if (keys.indexOf(keyToCheck) != -1) {
    //Key Exists logic;
}
else {
    //Key Not Found logic;
}
Davaid阳光小卤蛋2020/03/11 15:29:50

我认为这是一个小改进(成为1线):

   alert( test.level1 && test.level1.level2 && test.level1.level2.level3 )

之所以起作用,是因为&&运算符返回它求值的最终操作数(并使其短路)。

小宇宙路易2020/03/11 15:29:50

我知道这个问题很旧,但是我想通过将其添加到所有对象中来提供扩展。我知道人们倾向于使用对象原型来扩展对象功能,但是我发现没有比这更容易的事情了。另外,现在允许使用Object.defineProperty方法。

Object.defineProperty( Object.prototype, "has", { value: function( needle ) {
    var obj = this;
    var needles = needle.split( "." );
    for( var i = 0; i<needles.length; i++ ) {
        if( !obj.hasOwnProperty(needles[i])) {
            return false;
        }
        obj = obj[needles[i]];
    }
    return true;
}});

现在,为了测试任何对象中的任何属性,您可以简单地执行以下操作:

if( obj.has("some.deep.nested.object.somewhere") )

这是一个jsfiddle进行测试,特别是它包含一些jQuery,如果您直接修改Object.prototype会由于该属性变得可枚举而中断。这应该与第三方库一起正常工作。

神乐Gil2020/03/11 15:29:50

CMS给出的答案也可以很好地与以下对null检查的修改一起使用

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

            for (var i = 0; i < args.length; i++) 
            {
                if (obj == null || !obj.hasOwnProperty(args[i]) ) 
                {
                    return false;
                }
                obj = obj[args[i]];
            }
            return true;
    }
猿路易2020/03/11 15:29:50

这个答案开始阐述了以下选择两者的同一个树:

var o = { a: { b: { c: 1 } } };

未定义时停止搜索

var u = undefined;
o.a ? o.a.b ? o.a.b.c : u : u // 1
o.x ? o.x.y ? o.x.y.z : u : u // undefined
(o = o.a) ? (o = o.b) ? o.c : u : u // 1

确保每个级别一个接一个

var $ = function (empty) {
    return function (node) {
        return node || empty;
    };
}({});

$($(o.a).b).c // 1
$($(o.x).y).z // undefined
→笑里藏刀↓2020/03/11 15:29:50

创建一个function整体并在整个项目中使用

尝试这个

function isExist(arg){
   try{
      return arg();
   }catch(e){
      return false;
   }
}

let obj={a:5,b:{c:5}};

console.log(isExist(()=>obj.b.c))
console.log(isExist(()=>obj.b.foo))
console.log(isExist(()=>obj.test.foo))

如果条件

if(isExist(()=>obj.test.foo)){
   ....
}
阳光Itachi村村2020/03/11 15:29:50

基于这个答案,我想出了一个通用的函数来ES2015解决这个问题。

function validChain( object, ...keys ) {
    return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}

var test = {
  first: {
    second: {
        third: "This is not the key your are looking for"
    }
  }
}

if ( validChain( test, "first", "second", "third" ) ) {
    console.log( test.first.second.third );
}
神乐神乐2020/03/11 15:29:50

一种简单的方法是这样的:

try {
    alert(test.level1.level2.level3);
} catch(e) {
    alert("undefined");    // this is optional to put any output here
}

try/catch当未定义任何更高级别的对象(例如test,test.level1,test.level1.level2)时,捕获情况。

2020/03/11 15:29:50

您还可以将tc39可选链接建议与babel 7一起使用-tc39-proposal-optional-chaining

代码如下所示:

  const test = test?.level1?.level2?.level3;
  if (test) alert(test);
TonyPro2020/03/11 15:29:50

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

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

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

乐米亚2020/03/11 15:29:50

怎么样

try {
   alert(test.level1.level2.level3)
} catch(e) {
 ...whatever

}