如果我有对象的引用:

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

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


当前回答

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

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

其他回答

您可以使用递归函数来实现这一点。即使您不知道所有嵌套的对象键名称,也可以使用此方法。

function FetchKeys(obj) {
    let objKeys = [];
    let keyValues = Object.entries(obj);
    for (let i in keyValues) {
        objKeys.push(keyValues[i][0]);
        if (typeof keyValues[i][1] == "object") {
            var keys = FetchKeys(keyValues[i][1])
            objKeys = objKeys.concat(keys);
        }
    }
    return objKeys;
}

let test = { level1: { level2: { level3: "level3" } } };
let keyToCheck = "level2";
let keys = FetchKeys(test); //Will return an array of Keys

if (keys.indexOf(keyToCheck) != -1) {
    //Key Exists logic;
}
else {
    //Key Not Found logic;
}
var a;

a = {
    b: {
        c: 'd'
    }
};

function isset (fn) {
    var value;
    try {
        value = fn();
    } catch (e) {
        value = undefined;
    } finally {
        return value !== undefined;
    }
};

// ES5
console.log(
    isset(function () { return a.b.c; }),
    isset(function () { return a.b.c.d.e.f; })
);

如果您在ES6环境中编码(或使用6to5),则可以利用箭头函数语法:

// ES6 using the arrow function
console.log(
    isset(() => a.b.c),
    isset(() => a.b.c.d.e.f)
);

关于性能,使用try没有性能惩罚。。如果设置了属性,则捕获块。如果属性未设置,则会影响性能。

考虑简单地使用_.has:

var object = { 'a': { 'b': { 'c': 3 } } };

_.has(object, 'a');
// → true

_.has(object, 'a.b.c');
// → true

_.has(object, ['a', 'b', 'c']);
// → true

另一种解决方法是,例如,使用以下对象:

var x = {
    a: {
        b: 3
    }
};

然后,我所做的是向这个对象添加以下函数:

x.getKey = function(k){
        var r ;
        try {
            r = eval('typeof this.'+k+' !== "undefined"');
        }catch(e){
            r = false;
        }
        if(r !== false){
            return eval('this.'+k);
        }else{
            console.error('Missing key: \''+k+'\'');
            return '';
        }
    };

然后您可以测试:

x.getKey('a.b');

如果未定义,则函数返回“”(空字符串),否则返回现有值。

还请考虑检查链接的另一个更复杂的解决方案:JS对象具有属性深度检查

Object.prototype.hasOwnNestedProperty = function(propertyPath){
    if(!propertyPath)
        return false;

    var properties = propertyPath.split('.');
    var obj = this;

    for (var i = 0; i < properties.length; i++) {
        var prop = properties[i];

        if(!obj || !obj.hasOwnProperty(prop)){
            return false;
        } else {
            obj = obj[prop];
        }
    }

    return true;
};

// Usage: 
var obj = {
   innerObject:{
       deepObject:{
           value:'Here am I'
       }
   }
}

obj.hasOwnNestedProperty('innerObject.deepObject.value');

注:还有一个递归版本。

function isIn(string, object){
    var arr = string.split(".");
    var notFound = true;
    var length = arr.length;
    for (var i = 0; i < length; i++){
        var key = arr[i];
        if (!object.hasOwnProperty(key)){
            notFound = false;
            break;
        }
        if ((i + length) <= length){
            object = object[key];
        }
    }
    return notFound;
}
var musicCollection = {
    hasslehoff: {
        greatestHits : true
    }
};
console.log(isIn("hasslehoff.greatestHits", musicCollection));
console.log(isIn("hasslehoff.worseHits", musicCollection));

这里是我的基于字符串的分隔符版本。

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

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

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