如果我有对象的引用:

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

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


当前回答

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

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

其他回答

怎么样

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

}
//Just in case is not supported or not included by your framework
//***************************************************
Array.prototype.some = function(fn, thisObj) {
  var scope = thisObj || window;
  for ( var i=0, j=this.length; i < j; ++i ) {
    if ( fn.call(scope, this[i], i, this) ) {
      return true;
    }
  }
  return false;
};
//****************************************************

function isSet (object, string) {
  if (!object) return false;
  var childs = string.split('.');
  if (childs.length > 0 ) {
    return !childs.some(function (item) {
      if (item in object) {
        object = object[item]; 
        return false;
      } else return true;
    });
  } else if (string in object) { 
    return true;
  } else return false;
}

var object = {
  data: {
    item: {
      sub_item: {
        bla: {
          here : {
            iam: true
          }
        }
      }
    }
  }
};

console.log(isSet(object,'data.item')); // true
console.log(isSet(object,'x')); // false
console.log(isSet(object,'data.sub_item')); // false
console.log(isSet(object,'data.item')); // true
console.log(isSet(object,'data.item.sub_item.bla.here.iam')); // true

我在寻找属性存在时返回的值,所以我通过上面的CMS修改了答案。下面是我想到的:

函数getNestedProperty(obj,key){//从键字符串获取属性数组var财产=key.split(“.”);//遍历财产,如果对象为空或属性不存在,则返回未定义的对于(var i=0;i<属性.length;i++){if(!obj||!obj.hasOwnProperty(财产[i])){回来}obj=obj[财产[i]];}//找到嵌套属性,因此返回值返回obj;}用法:getNestedProperty(测试,“level.level2.level3”)//“level3”getNestedProperty(测试,“level.level2.foo”)//未定义

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

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

函数解析(目标){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)//如果它不存在,则会收到警告

我也遇到了同样的问题,我想看看是否能找到自己的解决方案。这接受要检查的路径作为字符串。

function checkPathForTruthy(obj, path) {
  if (/\[[a-zA-Z_]/.test(path)) {
    console.log("Cannot resolve variables in property accessors");
    return false;
  }

  path = path.replace(/\[/g, ".");
  path = path.replace(/]|'|"/g, "");
  path = path.split(".");

  var steps = 0;
  var lastRef = obj;
  var exists = path.every(key => {
    var currentItem = lastRef[path[steps]];
    if (currentItem) {
      lastRef = currentItem;
      steps++;
      return true;
    } else {
      return false;
    }
  });

  return exists;
}

下面是一些日志记录和测试用例的片段:

console.clear();var测试案例=[[“data.Messages[0].Code”,true],[“data.Messages[1].Code”,true],[“data.Messages[0]['Code']”,true],['data.Messages[0][“Code”]',true],[“data[Messages][0]['Code']”,错误],[“data['Messages'][0]['Code']”,真]];var path=“data.Messages[0].Code”;变量obj={数据:{消息:[{代码:“0”}, {代码:“1”}]}}函数checkPathForTruthy(obj,路径){if(/\[[a-zA-Z_]/.test(路径)){console.log(“无法解析属性访问器中的变量”);return false;}path=路径替换(/\[/g,“.”);path=路径替换(/]|'|“/g,”“);path=路径拆分(“.”);var步数=0;var lastRef=obj;var logOutput=[];var exists=path.every(key=>{var currentItem=lastRef[path[steps]];if(currentItem){logOutput.push(currentItem);lastRef=当前项;步骤++;返回true;}其他{return false;}});console.log(存在,logOutput);返回存在;}testCase.forEach(testCase=>{如果(checkPathForTruthy(obj,testCase[0])==testCase[1]){console.log(“通过:”+testCase[0]);}其他{console.log(“失败:”+testCase[0]+“预期”+testCase[1]);}});