如果我有对象的引用:

var test = {};

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

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

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

警报(测试级别1);生成未定义,但警告(test.level1.level2.level3);失败。

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

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

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


当前回答

答案很多,但仍然是:为什么不更简单?

获取值的es5版本为:

function value(obj, keys) {
    if (obj === undefined) return obj;
    if (keys.length === 1 && obj.hasOwnProperty(keys[0])) return obj[keys[0]];
    return value(obj[keys.shift()], keys);
}

if (value(test, ['level1', 'level2', 'level3'])) {
  // do something
}

您也可以将其与value(config,['applet',i,'height'])一起使用||42

CMS的ES6解决方案给了我这个想法。

其他回答

我尝试了递归方法:

function objHasKeys(obj, keys) {
  var next = keys.shift();
  return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}

这个keys.length||退出递归,这样它就不会在没有键可供测试的情况下运行函数。测验:

obj = {
  path: {
    to: {
      the: {
        goodKey: "hello"
      }
    }
  }
}

console.log(objHasKeys(obj, ['path', 'to', 'the', 'goodKey'])); // true
console.log(objHasKeys(obj, ['path', 'to', 'the', 'badKey']));  // undefined

我正在使用它打印一组具有未知键/值的对象的友好html视图,例如:

var biosName = objHasKeys(myObj, 'MachineInfo:BiosInfo:Name'.split(':'))
             ? myObj.MachineInfo.BiosInfo.Name
             : 'unknown';
/**
 * @method getValue
 * @description simplifies checking for existance and getting a deeply nested value within a ceratin context
 * @argument {string} s       string representation of the full path to the requested property 
 * @argument {object} context optional - the context to check defaults to window
 * @returns the value if valid and set, returns undefined if invalid / not available etc.
 */
var getValue = function( s, context ){
    var fn = function(){
        try{
            return eval(s);
        }catch(e){
            return undefined;
        }
    }
    return fn.call(context||window,s);
}

和用法:

if( getValue('a[0].b[0].b[0].d') == 2 ) // true

我想我应该再加一个我今天想到的。我对这个解决方案感到自豪的原因是它避免了许多解决方案中使用的嵌套括号,例如Object Wrap(Oliver Steele):

(在本例中,我使用下划线作为占位符变量,但任何变量名称都有效)

//“测试”对象var测试={level1:{level2:{level3:‘level3‘}}}};设_=测试;如果((_=_level1)&&(_=.level2)&&{设level3=_;//做3级的事情}

//您也可以使用“stacked”if语句。如果你的物体很深,这会有帮助。//(除最后一个大括号外,不带嵌套或大括号)设_=测试;如果(_=_.level1)如果(_=_.level2)如果(_=_.level3){设level3=_;//做3级的事情}//或者可以缩进:如果(_=_.level1)如果(_=_.level2)如果(_=_.level3){设level3=_;//做3级的事情}

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

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

这是我使用的一个小助手函数,对我来说,它非常简单明了。希望这对一些人有帮助:)。

static issetFromIndices(param, indices, throwException = false) {
    var temp = param;

    try {
        if (!param) {
            throw "Parameter is null.";
        }

        if(!Array.isArray(indices)) {
            throw "Indices parameter must be an array.";
        }

        for (var i = 0; i < indices.length; i++) {
            var index = indices[i];
            if (typeof temp[index] === "undefined") {
                throw "'" + index + "' index is undefined.";
            }


            temp = temp[index];
        }
    } catch (e) {
        if (throwException) {
            throw new Error(e);
        } else {
            return false;
        }
    }

    return temp;
}

var person = {
    hobbies: {
        guitar: {
            type: "electric"
        }
    }
};

var indices = ["hobbies", "guitar", "type"];
var throwException = true;

try {
    var hobbyGuitarType = issetFromIndices(person, indices, throwException);
    console.log("Yay, found index: " + hobbyGuitarType);
} catch(e) {
    console.log(e);
}