如果我有对象的引用:

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

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


当前回答

今天刚刚编写了这个函数,它对嵌套对象中的属性进行了深入搜索,如果找到了,则返回该属性的值。

/**
 * Performs a deep search looking for the existence of a property in a 
 * nested object. Supports namespaced search: Passing a string with
 * a parent sub-object where the property key may exist speeds up
 * search, for instance: Say you have a nested object and you know for 
 * certain the property/literal you're looking for is within a certain
 * sub-object, you can speed the search up by passing "level2Obj.targetProp"
 * @param {object} obj Object to search
 * @param {object} key Key to search for
 * @return {*} Returns the value (if any) located at the key
 */
var getPropByKey = function( obj, key ) {
    var ret = false, ns = key.split("."),
        args = arguments,
        alen = args.length;

    // Search starting with provided namespace
    if ( ns.length > 1 ) {
        obj = (libName).getPropByKey( obj, ns[0] );
        key = ns[1];
    }

    // Look for a property in the object
    if ( key in obj ) {
        return obj[key];
    } else {
        for ( var o in obj ) {
            if ( (libName).isPlainObject( obj[o] ) ) {
                ret = (libName).getPropByKey( obj[o], key );
                if ( ret === 0 || ret === undefined || ret ) {
                    return ret;
                }
            }
        }
    }

    return false;
}

其他回答

一个简短的ES5版本@CMS的优秀答案:

// Check the obj has the keys in the order mentioned. Used for checking JSON results.  
var checkObjHasKeys = function(obj, keys) {
  var success = true;
  keys.forEach( function(key) {
    if ( ! obj.hasOwnProperty(key)) {
      success = false;
    }
    obj = obj[key];
  })
  return success;
}

通过类似测试:

var test = { level1:{level2:{level3:'result'}}};
utils.checkObjHasKeys(test, ['level1', 'level2', 'level3']); // true
utils.checkObjHasKeys(test, ['level1', 'level2', 'foo']); // false

我已经对这个问题提出的一些建议进行了性能测试(感谢cdMinix添加了lodash),结果如下。

免责声明#1将字符串转换为引用是不必要的元编程,可能最好避免。首先不要忘记你的推荐人。阅读类似问题的答案。免责声明#2我们在这里谈论的是每毫秒数百万次的操作。在大多数用例中,这些都不太可能产生很大的差异。了解每种方法的局限性,选择最有意义的方法。对我来说,我会采取一些类似于出于方便而减少的措施。

物体包裹(Oliver Steele)–34%–最快

var r1 = (((test || {}).level1 || {}).level2 || {}).level3;
var r2 = (((test || {}).level1 || {}).level2 || {}).foo;

原始解决方案(有疑问的建议)–45%

var r1 = test.level1 && test.level1.level2 && test.level1.level2.level3;
var r2 = test.level1 && test.level1.level2 && test.level1.level2.foo;

checkNested–50%

function checkNested(obj) {
  for (var i = 1; i < arguments.length; i++) {
    if (!obj.hasOwnProperty(arguments[i])) {
      return false;
    }
    obj = obj[arguments[i]];
  }
  return true;
}

get_if_exist–52%

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

有效链–54%

function validChain( object, ...keys ) {
    return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}

objHasKeys–63%

function objHasKeys(obj, keys) {
  var next = keys.shift();
  return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}

nestedPropertyExists–69%

function nestedPropertyExists(obj, props) {
    var prop = props.shift();
    return prop === undefined ? true : obj.hasOwnProperty(prop) ? nestedPropertyExists(obj[prop], props) : false;
}

_.获得–72%

深度测试–86%

function deeptest(target, s){
    s= s.split('.')
    var obj= target[s.shift()];
    while(obj && s.length) obj= obj[s.shift()];
    return obj;
}

悲伤的小丑–100%–最慢

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

var r1 = o(o(o(o(test).level1).level2).level3);
var r2 = o(o(o(o(test).level1).level2).foo);

这个功能怎么样?它不需要单独列出每个嵌套属性,而是保持“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);

    }

CMS解决方案工作得很好,但使用/语法可能更方便。我建议如下

var checkNested = function(obj, structure) {

  var args = structure.split(".");

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

您可以简单地使用使用点的对象表示法,而不是提供多个参数

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

checkNested(test, 'level1.level2.level3'); // true
checkNested(test, 'level1.level2.foo'); // false
getValue (o, key1, key2, key3, key4, key5) {
    try {
      return o[key1][key2][key3][key4][key5]
    } catch (e) {
      return null
    }
}