如果我有对象的引用:

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

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


当前回答

如果您恰好使用AngularJs,则可以使用$parse服务检查是否存在深度对象属性,如下所示:

if( $parse('model.data.items')(vm) ) {
    vm.model.data.items.push('whatever');
}

为了避免这样的陈述:

if(vm.model && vm.model.data && vm.model.data.items) {
    ....
}

不要忘记将$parse服务注入控制器

有关详细信息:https://glebbahmutov.com/blog/angularjs-parse-hacks/

其他回答

这适用于所有对象和阵列:)

ex:

if( obj._has( "something.['deep']['under'][1][0].item" ) ) {
    //do something
}

这是我对Brian答案的改进版

我使用_has作为属性名称,因为它可能与现有的has属性(例如:maps)冲突

Object.defineProperty( Object.prototype, "_has", { value: function( needle ) {
var obj = this;
var needles = needle.split( "." );
var needles_full=[];
var needles_square;
for( var i = 0; i<needles.length; i++ ) {
    needles_square = needles[i].split( "[" );
    if(needles_square.length>1){
        for( var j = 0; j<needles_square.length; j++ ) {
            if(needles_square[j].length){
                needles_full.push(needles_square[j]);
            }
        }
    }else{
        needles_full.push(needles[i]);
    }
}
for( var i = 0; i<needles_full.length; i++ ) {
    var res = needles_full[i].match(/^((\d+)|"(.+)"|'(.+)')\]$/);
    if (res != null) {
        for (var j = 0; j < res.length; j++) {
            if (res[j] != undefined) {
                needles_full[i] = res[j];
            }
        }
    }

    if( typeof obj[needles_full[i]]=='undefined') {
        return false;
    }
    obj = obj[needles_full[i]];
}
return true;
}});

这是小提琴

在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;}})}返回闭合(输入);}

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

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

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

获取值的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解决方案给了我这个想法。

现在,我们还可以使用reduce循环嵌套键:

//@params o<对象>//@params路径<string>应为“obj.prop1.prop2.prop3”//返回:obj[path]值或“false”(如果prop不存在)const objPropIfExists=o=>路径=>{常量级别=路径.split('.');常量res=(levels.length>0)? level.reduce((a,c)=>a[c]||0,o):o[路径];return(!!res)?res:假}常量对象={name:'名称',sys:{country:“AU”},main:{temp:“34”,temp_min:“13”},能见度:“35%”}const exists=objPropIfExists(obj)('main.temp')const doesntExist=objPropIfExists(obj)('main.temp.foo.baz')console.log(存在,不存在)