如果我有对象的引用:

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);
}

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


当前回答

我没有看到任何人使用代理的例子

所以我想出了自己的办法。它的优点是你不必对字符串进行插值。实际上,您可以返回一个可链接的对象函数并用它做一些神奇的事情

函数解析(目标){var noop=()=>{}//我们使用了一个noop函数,因此我们也可以调用方法返回新代理(noop{获取(noop,key){//如果键为_result,则返回最终结果返回键==“_result”? 目标:resolve(//使用目标值或未定义的值进行解析目标==未定义?未定义:目标[key])},//如果我们想测试一个函数,那么由于使用noop,我们可以这么做//而不是在代理中使用target应用(noop,that,args){返回解析(typeof target==“function”?target.apply(that,args):未定义)},})}//接受答案中的一些修改示例var测试={level1:{level2:()=>({level3:'level3'})}}var test1={key1:{key2:['item0‘]}}//您需要在最后获得_result以获得最终结果console.log(解析(测试).level1.level2().level3._result)console.log(解析(测试).level1.level2().level3.level4.level5._result)console.log(解析(test1).key1.key2[0].result)console.log(resolve(test1)[0].key.result)//不存在

以上代码适用于同步内容。但是,如何测试像ajax调用这样的异步功能呢?你是怎么测试的?

fetch('https://httpbin.org/get')
.then(function(response) {
  return response.json()
})
.then(function(json) {
  console.log(json.headers['User-Agent'])
})

确保可以使用async/await来消除一些回调。但如果你能做得更神奇呢?看起来像这样的东西:

fetch('https://httpbin.org/get').json().headers['User-Agent']

你可能会想知道所有的承诺和链条在哪里。。。这可能会阻碍你所知道的一切。。。但使用相同的代理技术,您实际上可以测试深度嵌套的复杂路径是否存在,而无需编写单个函数

函数解析(目标){返回新代理(()=>{}{获取(noop,key){return key==“then”?target.then.bind(target):解析(承诺.解决(目标).然后(目标=>{if(typeof target[key]==“function”)返回target[key].bind(target)返回目标[键]}))},应用(noop,that,args){返回解析(target.then(result=>{返回result.apply(即,args)}))},})}//这感觉非常同步,但仍然是非阻塞的:)resolve(window)//这将链接一个noop函数,直到您调用then().fetch('https://httpbin.org/get').json().headers[“用户代理”].then(console.log,console.warn)//如果它不存在,则会收到警告//也可以对第一个测试对象使用此方法//也可以,但最后必须调用.then()//另一个例子解析(窗口).fetch('https://httpbin.org/get?items=4&items=2').json().args参数.个项目//很好,您可以在没有准备好的情况下映射数组项.map(n=>~n*4).then(console.log,console.warn)//如果它不存在,则会收到警告

其他回答

怎么样

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

}

我已经对这个问题提出的一些建议进行了性能测试(感谢cdMinix添加了lodash),结果如下。

免责声明#1将字符串转换为引用是不必要的元编程,可能最好避免。首先不要忘记你的推荐人。阅读类似问题的答案。免责声明#2我们在这里谈论的是每毫秒数百万次的操作。在大多数用例中,这些都不太可能产生很大的差异。了解每种方法的局限性,选择最有意义的方法。对我来说,我会采取一些类似于出于方便而减少的措施。

物体包裹(Oliver Steele)–34%–最快

var r1 = (((test || {}).level1 || {}).level2 || {}).level3;
var r2 = (((test || {}).level1 || {}).level2 || {}).foo;

原始解决方案(有疑问的建议)–45%

var r1 = test.level1 && test.level1.level2 && test.level1.level2.level3;
var r2 = test.level1 && test.level1.level2 && test.level1.level2.foo;

checkNested–50%

function checkNested(obj) {
  for (var i = 1; i < arguments.length; i++) {
    if (!obj.hasOwnProperty(arguments[i])) {
      return false;
    }
    obj = obj[arguments[i]];
  }
  return true;
}

get_if_exist–52%

function get_if_exist(str) {
    try { return eval(str) }
    catch(e) { return undefined }
}

有效链–54%

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

objHasKeys–63%

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

nestedPropertyExists–69%

function nestedPropertyExists(obj, props) {
    var prop = props.shift();
    return prop === undefined ? true : obj.hasOwnProperty(prop) ? nestedPropertyExists(obj[prop], props) : false;
}

_.获得–72%

深度测试–86%

function deeptest(target, s){
    s= s.split('.')
    var obj= target[s.shift()];
    while(obj && s.length) obj= obj[s.shift()];
    return obj;
}

悲伤的小丑–100%–最慢

var o = function(obj) { return obj || {} };

var r1 = o(o(o(o(test).level1).level2).level3);
var r2 = o(o(o(o(test).level1).level2).foo);

根据之前的评论,这里是另一个无法定义主对象的版本:

// Supposing that our property is at first.second.third.property:
var property = (((typeof first !== 'undefined' ? first : {}).second || {}).third || {}).property;

基于这个答案,我使用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 );
}

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

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