如果我有对象的引用:

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

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


当前回答

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

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

我认为下面的脚本提供了更可读的表示。

声明函数:

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

然后这样使用:

if (o(o(o(o(test).level1).level2).level3)
{

}

我称之为“悲伤小丑技巧”,因为它使用了符号o(


编辑:

这是TypeScript的版本

它在编译时提供类型检查(如果使用像Visual Studio这样的工具,还提供智能感知)

export function o<T>(someObject: T, defaultValue: T = {} as T) : T {
    if (typeof someObject === 'undefined' || someObject === null)
        return defaultValue;
    else
        return someObject;
}

用法相同:

o(o(o(o(test).level1).level2).level3

但这次intelligense奏效了!

此外,您可以设置默认值:

o(o(o(o(o(test).level1).level2).level3, "none")

在html模板中使用一行程序并没有真正好的答案,所以我使用ES6代理做了一个。您只需将一个对象或值传递给“遍历”函数,并使用返回值或回退值的函数调用来完成所需的嵌套调用。使用:

const testObject={深度:{嵌套:{对象:{closure:()=>{return“closure”},数量:9,布尔值:真,数组:[1,2,{foo:{bar:true}}]} }}}遍历(testObject).dep()//{嵌套:{…}}遍历(testObject).non.existint()//未定义的遍历(testObject).dep.enested.obj.closure()//关闭遍历(testObject).dep.enested.obj.array[5]('fallback')//后退,后退遍历(testObject).dep.enested.obj.array[2]()//{foo:{…}}遍历(testObject).dep.enested.obj.array[2].foo.bar()//真的遍历(testObject).dept.netnested.obj.array[2].foo.bar[4]('fallback')//后退,后退遍历(testObject).完全.错误[3].调用().WILL_THROW()//未捕获的TypeError:无法读取未定义的属性“WILL_TROW”

函数本身:

常量遍历=(输入)=>{//唯一空对象const unset=新对象();//我们需要包装器来确保我们可以访问同一个唯一的空对象常量闭包=(输入)=>{//将每个输入包装到此const handler=new Function();handler.input=输入;//返回包装器代理返回新代理(handler{//保持横向移动获取:(target,name)=>{//如果未定义,则作为初始输入提供if(!target.input){返回关闭(未设置);}//否则if(target.input[name]!==未定义){//输入具有该属性返回闭包(target.input[name]);}其他{返回关闭(未设置);}},//带回退的结果apply:(目标,上下文,参数)=>{return handler.input==未设置?args[0]:handler.input;}})}返回闭合(输入);}

这是我从奥利弗·斯蒂尔那里学到的一个模式:

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

事实上,整篇文章讨论了如何在javascript中实现这一点。他决定使用上面的语法(一旦你习惯了,它就不那么难读了)作为成语。

我认为这是一个轻微的改进(变成了一行):

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

这之所以有效,是因为&&运算符返回其计算的最终操作数(并且短路)。