如果我有对象的引用:

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

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


当前回答

/**
 * @method getValue
 * @description simplifies checking for existance and getting a deeply nested value within a ceratin context
 * @argument {string} s       string representation of the full path to the requested property 
 * @argument {object} context optional - the context to check defaults to window
 * @returns the value if valid and set, returns undefined if invalid / not available etc.
 */
var getValue = function( s, context ){
    var fn = function(){
        try{
            return eval(s);
        }catch(e){
            return undefined;
        }
    }
    return fn.call(context||window,s);
}

和用法:

if( getValue('a[0].b[0].b[0].d') == 2 ) // true

其他回答

我也遇到了同样的问题,我想看看是否能找到自己的解决方案。这接受要检查的路径作为字符串。

function checkPathForTruthy(obj, path) {
  if (/\[[a-zA-Z_]/.test(path)) {
    console.log("Cannot resolve variables in property accessors");
    return false;
  }

  path = path.replace(/\[/g, ".");
  path = path.replace(/]|'|"/g, "");
  path = path.split(".");

  var steps = 0;
  var lastRef = obj;
  var exists = path.every(key => {
    var currentItem = lastRef[path[steps]];
    if (currentItem) {
      lastRef = currentItem;
      steps++;
      return true;
    } else {
      return false;
    }
  });

  return exists;
}

下面是一些日志记录和测试用例的片段:

console.clear();var测试案例=[[“data.Messages[0].Code”,true],[“data.Messages[1].Code”,true],[“data.Messages[0]['Code']”,true],['data.Messages[0][“Code”]',true],[“data[Messages][0]['Code']”,错误],[“data['Messages'][0]['Code']”,真]];var path=“data.Messages[0].Code”;变量obj={数据:{消息:[{代码:“0”}, {代码:“1”}]}}函数checkPathForTruthy(obj,路径){if(/\[[a-zA-Z_]/.test(路径)){console.log(“无法解析属性访问器中的变量”);return false;}path=路径替换(/\[/g,“.”);path=路径替换(/]|'|“/g,”“);path=路径拆分(“.”);var步数=0;var lastRef=obj;var logOutput=[];var exists=path.every(key=>{var currentItem=lastRef[path[steps]];if(currentItem){logOutput.push(currentItem);lastRef=当前项;步骤++;返回true;}其他{return false;}});console.log(存在,logOutput);返回存在;}testCase.forEach(testCase=>{如果(checkPathForTruthy(obj,testCase[0])==testCase[1]){console.log(“通过:”+testCase[0]);}其他{console.log(“失败:”+testCase[0]+“预期”+testCase[1]);}});

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

    }

一个简短的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

我想我应该再加一个我今天想到的。我对这个解决方案感到自豪的原因是它避免了许多解决方案中使用的嵌套括号,例如Object Wrap(Oliver Steele):

(在本例中,我使用下划线作为占位符变量,但任何变量名称都有效)

//“测试”对象var测试={level1:{level2:{level3:‘level3‘}}}};设_=测试;如果((_=_level1)&&(_=.level2)&&{设level3=_;//做3级的事情}

//您也可以使用“stacked”if语句。如果你的物体很深,这会有帮助。//(除最后一个大括号外,不带嵌套或大括号)设_=测试;如果(_=_.level1)如果(_=_.level2)如果(_=_.level3){设level3=_;//做3级的事情}//或者可以缩进:如果(_=_.level1)如果(_=_.level2)如果(_=_.level3){设level3=_;//做3级的事情}

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