如果我有对象的引用:

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

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


当前回答

这个功能怎么样?它不需要单独列出每个嵌套属性,而是保持“dot”语法(尽管是字符串),使其更具可读性。如果未找到属性,则返回undefined或指定的默认值,如果找到,则返回属性的值。

val(obj, element, default_value)
    // Recursively checks whether a property of an object exists. Supports multiple-level nested properties separated with '.' characters.
    // obj = the object to test
    // element = (string or array) the name of the element to test for.  To test for a multi-level nested property, separate properties with '.' characters or pass as array)
    // default_value = optional default value to return if the item is not found. Returns undefined if no default_value is specified.
    // Returns the element if it exists, or undefined or optional default_value if not found.
    // Examples: val(obj1, 'prop1.subprop1.subsubprop2');
    // val(obj2, 'p.r.o.p', 'default_value');
    {

        // If no element is being requested, return obj. (ends recursion - exists)
        if (!element || element.length == 0) { return obj; }

        // if the element isn't an object, then it can't have properties. (ends recursion - does not exist)
        if (typeof obj != 'object') { return default_value; }

        // Convert element to array.
        if (typeof element == 'string') { element = element.split('.') };   // Split on dot (.)

        // Recurse into the list of nested properties:
        let first = element.shift();
        return val(obj[first], element, default_value);

    }

其他回答

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

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

注:还有一个递归版本。

这个问题由来已久。今天,您可以使用可选链接(?.)

let value = test?.level1?.level2?.level3;

资料来源:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining

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

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

这是小提琴

还有一个非常紧凑的:

function ifSet(object, path) {
  return path.split('.').reduce((obj, part) => obj && obj[part], object)
}

打电话:

let a = {b:{c:{d:{e:'found!'}}}}
ifSet(a, 'b.c.d.e') == 'found!'
ifSet(a, 'a.a.a.a.a.a') == undefined

它的性能不会很好,因为它拆分了一个字符串(但增加了调用的可读性),并遍历所有内容,即使已经很明显找不到任何内容(但提高了函数本身的可读性。

至少比get快http://jsben.ch/aAtmc

这个功能怎么样?它不需要单独列出每个嵌套属性,而是保持“dot”语法(尽管是字符串),使其更具可读性。如果未找到属性,则返回undefined或指定的默认值,如果找到,则返回属性的值。

val(obj, element, default_value)
    // Recursively checks whether a property of an object exists. Supports multiple-level nested properties separated with '.' characters.
    // obj = the object to test
    // element = (string or array) the name of the element to test for.  To test for a multi-level nested property, separate properties with '.' characters or pass as array)
    // default_value = optional default value to return if the item is not found. Returns undefined if no default_value is specified.
    // Returns the element if it exists, or undefined or optional default_value if not found.
    // Examples: val(obj1, 'prop1.subprop1.subsubprop2');
    // val(obj2, 'p.r.o.p', 'default_value');
    {

        // If no element is being requested, return obj. (ends recursion - exists)
        if (!element || element.length == 0) { return obj; }

        // if the element isn't an object, then it can't have properties. (ends recursion - does not exist)
        if (typeof obj != 'object') { return default_value; }

        // Convert element to array.
        if (typeof element == 'string') { element = element.split('.') };   // Split on dot (.)

        // Recurse into the list of nested properties:
        let first = element.shift();
        return val(obj[first], element, default_value);

    }