如果我有对象的引用:

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

其他回答

CMS给出的答案也适用于空检查的以下修改

function checkNested(obj /*, level1, level2, ... levelN*/) 
      {
             var args = Array.prototype.slice.call(arguments),
             obj = args.shift();

            for (var i = 0; i < args.length; i++) 
            {
                if (obj == null || !obj.hasOwnProperty(args[i]) ) 
                {
                    return false;
                }
                obj = obj[args[i]];
            }
            return true;
    }

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

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

这是小提琴

现在,我们还可以使用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(存在,不存在)

我的解决方案,我使用了很长时间(使用字符串不幸,找不到更好的)

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

// way to use
if(get_if_exist('test.level1.level2.level3')) {
    alert(test.level1.level2.level3);
}

// or simply 
alert(get_if_exist('test.level1.level2.level3'));

edit:只有当对象“test”具有全局范围/范围时,这才有效。否则您必须执行以下操作:

// i think it's the most beautiful code I have ever write :p
function get_if_exist(obj){
    return arguments.length==1 || (obj[arguments[1]] && get_if_exist.apply(this,[obj[arguments[1]]].concat([].slice.call(arguments,2))));
}

alert(get_if_exist(test,'level1','level2','level3'));

编辑最终版本以允许2种调用方法:

function get_if_exist(obj){
    var a=arguments, b=a.callee; // replace a.callee by the function name you choose because callee is depreceate, in this case : get_if_exist
    // version 1 calling the version 2
    if(a[1] && ~a[1].indexOf('.')) 
        return b.apply(this,[obj].concat(a[1].split('.')));
    // version 2
    return a.length==1 ? a[0] : (obj[a[1]] && b.apply(this,[obj[a[1]]].concat([].slice.call(a,2))));
}

// method 1
get_if_exist(test,'level1.level2.level3');


// method 2
get_if_exist(test,'level1','level2','level3');

您可以尝试可选链接(但要注意浏览器兼容性)。

let test = {level1: {level2: {level3: 'level3'}}};

let level3 = test?.level1?.level2?.level3;
console.log(level3); // level3

level3 = test?.level0?.level1?.level2?.level3;
console.log(level3); // undefined

有一个babel插件(@babel/plugin建议可选链接)用于光学通道。所以,如果需要,请升级您的babel。