首页 文章

测试是否存在嵌套的JavaScript对象键

提问于
浏览
489

如果我有一个对象的引用:

var test = {};

可能(但不是立即)有嵌套对象,如:

{level1: {level2: {level3: "level3"}}};

在最深层嵌套的对象中测试密钥是否存在的最佳方法是什么?

alert(test.level1); 收益 undefined ,但 alert(test.level1.level2.level3); 失败 .

我现在正在做这样的事情:

if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
    alert(test.level1.level2.level3);
}

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

30 回答

  • 6

    如果您像字符串一样处理名称,则可以读取任何深度的对象属性: 't.level1.level2.level3' .

    window.t={level1:{level2:{level3: 'level3'}}};
    
    function deeptest(s){
        s= s.split('.')
        var obj= window[s.shift()];
        while(obj && s.length) obj= obj[s.shift()];
        return obj;
    }
    
    alert(deeptest('t.level1.level2.level3') || 'Undefined');
    

    如果任何段为 undefined ,则返回 undefined .

  • 2

    基于a previous comment,这是另一个版本,其中主要对象也无法定义:

    // Supposing that our property is at first.second.third.property:
    var property = (((typeof first !== 'undefined' ? first : {}).second || {}).third || {}).property;
    
  • 3

    如果您不想要 TypeError ,则必须一步一步地执行此操作,因为如果其中一个成员是 nullundefined ,并且您尝试访问某个成员,则会抛出异常 .

    您可以简单地 catch 异常,或者创建一个函数来测试多个级别的存在,如下所示:

    function checkNested(obj /*, level1, level2, ... levelN*/) {
      var args = Array.prototype.slice.call(arguments, 1);
    
      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
    
  • 2

    这是一个模式我picked up from Oliver Steele

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

    事实上,整篇文章讨论了如何在javascript中执行此操作 . 他习惯于使用上面的语法(一旦你习惯它就不难读)作为一个习语 .

  • 1

    更新

    对于所有需要的嵌套属性,看起来像lodash has added _.get .

    _.get(countries, 'greece.sparta.playwright')
    

    https://lodash.com/docs#get


    以前的答案

    lodash用户可能喜欢lodash.contrib,其中有couple methods that mitigate this problem .

    getPath

    Signature: _.getPath(obj:Object, ks:String|Array)

    获取嵌套对象中任何深度的值,该值基于给定键所描述的路径 . 键可以作为数组或以点分隔的字符串给出 . 如果无法到达路径,则返回 undefined .

    var countries = {
            greece: {
                athens: {
                    playwright:  "Sophocles"
                }
            }
        }
    };
    
    _.getPath(countries, "greece.athens.playwright");
    // => "Sophocles"
    
    _.getPath(countries, "greece.sparta.playwright");
    // => undefined
    
    _.getPath(countries, ["greece", "athens", "playwright"]);
    // => "Sophocles"
    
    _.getPath(countries, ["greece", "sparta", "playwright"]);
    // => undefined
    
  • 2

    我已经完成了performance tests(感谢cdMinix添加lodash)对这个问题提出的一些建议,结果如下所示 .

    免责声明#1将字符串转换为引用是不必要的元编程,最好避免使用 . 不要忘记开头的参考资料 . 从这个问题的答案中读取更多内容 . 免责声明#2我们在谈论每毫秒数百万次操作 . 在大多数用例中,这些都不太可能产生太大的影响 . 了解每个人的局限性,选择哪个最有意义 . 对我来说,我会选择减少方便之类的东西 .

    Object Wrap (by Oliver Steele) – 34 % – fastest

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

    Original solution (suggested in question) – 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 }
    }
    

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

    _.get – 72%

    deeptest – 86%

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

    sad clowns – 100% – slowest

    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);
    
  • 206
    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),那么您可以利用arrow function语法:

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

    关于性能,如果设置了属性,则使用 try..catch 块不会有性能损失 . 如果未设置该属性,则会对性能产生影响 .

    考虑简单地使用_.has

    var object = { 'a': { 'b': { 'c': 3 } } };
    
    _.has(object, 'a');
    // → true
    
    _.has(object, 'a.b.c');
    // → true
    
    _.has(object, ['a', 'b', 'c']);
    // → true
    
  • 3

    怎么样

    try {
       alert(test.level1.level2.level3)
    } catch(e) {
     ...whatever
    
    }
    
  • 8

    ES6答案,经过彻底测试:)

    const propExists = (obj, path) => {
        return !!path.split('.').reduce((obj, prop) => {
            return obj && obj[prop] ? obj[prop] : undefined;
        }, obj)
    }
    

    →查看具有完整测试覆盖率的Codepen

  • 2

    我尝试了一种递归方法:

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

    ! keys.length || 从递归开始,因此它不会运行该函数而没有剩余的密钥进行测试 . 测试:

    obj = {
      path: {
        to: {
          the: {
            goodKey: "hello"
          }
        }
      }
    }
    
    console.log(objHasKeys(obj, ['path', 'to', 'the', 'goodKey'])); // true
    console.log(objHasKeys(obj, ['path', 'to', 'the', 'badKey']));  // undefined
    

    我用它来打印一堆具有未知键/值的对象的友好html视图,例如:

    var biosName = objHasKeys(myObj, 'MachineInfo:BiosInfo:Name'.split(':'))
                 ? myObj.MachineInfo.BiosInfo.Name
                 : 'unknown';
    
  • 17

    你也可以使用tc39可选链接提议和babel 7 - tc39-proposal-optional-chaining

    代码看起来像这样:

    const test = test?.level1?.level2?.level3;
      if (test) alert(test);
    
  • 2

    我认为以下脚本提供了更具可读性的表示 .

    声明一个函数:

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

    然后像这样使用它:

    if (o(o(o(o(test).level1).level2).level3)
    {
    
    }
    

    我称之为“悲伤的小丑技术”,因为它使用的是标志o(


    编辑:

    这是 TypeScript 的一个版本

    它在编译时提供类型检查(如果你使用像Visual Studio这样的工具,还会提供intellisense)

    export function o<T>(someObject: T, defaultValue: T = {} as T) : T {
        if (typeof someObject === 'undefined' || someObject === null)
            return defaultValue;
        else
            return someObject;
    }
    

    用法是一样的:

    o(o(o(o(test).level1).level2).level3
    

    但这次intellisense工作!

    另外,您可以设置默认值:

    o(o(o(o(o(test).level1).level2).level3, "none")
    
  • 1

    我没有看到任何使用Proxies的人的例子

    所以我想出了自己的 . 关于它的好处是你不必插入字符串 . 你实际上可以返回一个可链式对象函数并用它做一些神奇的事情 . 您甚至可以调用函数并获取数组索引来检查深层对象

    function resolve(target) {
      var noop = () => {} // We us a noop function so we can call methods also
      return new Proxy(noop, {
        get(noop, key) {
          // return end result if key is _result
          return key === '_result' 
            ? target 
            : resolve( // resolve with target value or undefined
                target === undefined ? undefined : target[key]
              )
        },
    
        // if we want to test a function then we can do so alos thanks to using noop
        // instead of using target in our proxy
        apply(noop, that, args) {
          return resolve(typeof target === 'function' ? target.apply(that, args) : undefined)
        },
      })
    }
    
    // some modified examples from the accepted answer
    var test = {level1: {level2:() => ({level3:'level3'})}}
    var test1 = {key1: {key2: ['item0']}}
    
    // You need to get _result in the end to get the final result
    
    console.log(resolve(test).level1.level2().level3._result)
    console.log(resolve(test).level1.level2().level3.level4.level5._result)
    console.log(resolve(test1).key1.key2[0]._result)
    console.log(resolve(test1)[0].key._result) // don't exist
    

    上面的代码适用于同步的东西 . 但是你如何测试像ajax调用那样异步的东西呢?你是如何测试的?如果在返回500 http错误时响应不是json怎么办?

    window.fetch('https://httpbin.org/get')
    .then(function(response) {
      return response.json()
    })
    .then(function(json) {
      console.log(json.headers['User-Agent'])
    })
    

    确定你可以使用async / await来摆脱一些回调 . 但是,如果你能做得更神奇呢?看起来像这样的东西:

    fetch('https://httpbin.org/get').json().headers['User-Agent']
    

    你可能想知道所有的承诺和 .then 链在哪里......这可能会阻塞所有你知道的...但是使用相同的代理技术和承诺你可以实际测试深层嵌套的复杂路径,而不需要写一个功能

    function resolve(target) { 
      return new Proxy(() => {}, {
        get(noop, key) {
          return key === 'then' ? target.then.bind(target) : resolve(
            Promise.resolve(target).then(target => {
              if (typeof target[key] === 'function') return target[key].bind(target)
              return target[key]
            })
          )
        },
    
        apply(noop, that, args) {
          return resolve(target.then(result => {
            return result.apply(that, args)
          }))
        },
      })
    }
    
    // this feels very much synchronous but are still non blocking :)
    resolve(window) // this will chain a noop function until you call then()
      .fetch('https://httpbin.org/get')
      .json()
      .headers['User-Agent']
      .then(console.log, console.warn) // you get a warning if it doesn't exist
      
    // You could use this method also for the first test object
    // also, but it would have to call .then() in the end
    
    
    
    // Another example
    resolve(window)
      .fetch('https://httpbin.org/get?items=4&items=2')
      .json()
      .args
      .items
      // nice that you can map an array item without even having it ready
      .map(n => ~~n * 4) 
      .then(console.log, console.warn) // you get a warning if it doesn't exist
    
  • 1

    一个简单的方法是:

    try {
        alert(test.level1.level2.level3);
    } catch(e) {
        alert("undefined");    // this is optional to put any output here
    }
    

    try/catch 捕获任何更高级别对象(如test,test.level1,test.level1.level2)未定义的情况 .

  • 2

    一个较短的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
    
  • 2

    基于this answer,我使用 ES2015 想出了这个通用函数,它可以解决问题

    function validChain( object, ...keys ) {
        return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
    }
    
    var test = {
      first: {
        second: {
            third: "This is not the key your are looking for"
        }
      }
    }
    
    if ( validChain( test, "first", "second", "third" ) ) {
        console.log( test.first.second.third );
    }
    
  • 23

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

    以下选项从this answer开始详细阐述 . 同一棵树:

    var o = { a: { b: { c: 1 } } };
    

    未定义时停止搜索

    var u = undefined;
    o.a ? o.a.b ? o.a.b.c : u : u // 1
    o.x ? o.x.y ? o.x.y.z : u : u // undefined
    (o = o.a) ? (o = o.b) ? o.c : u : u // 1
    

    逐个确保每个级别

    var $ = function (empty) {
        return function (node) {
            return node || empty;
        };
    }({});
    
    $($(o.a).b).c // 1
    $($(o.x).y).z // undefined
    
  • 306

    我知道这个问题很老,但我希望通过将其添加到所有对象来提供扩展 . 我知道人们倾向于使用Object原型来扩展对象功能,但我现在不允许Object.defineProperty方法使用Object.defineProperty方法 .

    Object.defineProperty( Object.prototype, "has", { value: function( needle ) {
        var obj = this;
        var needles = needle.split( "." );
        for( var i = 0; i<needles.length; i++ ) {
            if( !obj.hasOwnProperty(needles[i])) {
                return false;
            }
            obj = obj[needles[i]];
        }
        return true;
    }});
    

    现在,为了测试任何对象中的任何属性,您可以简单地执行:

    if( obj.has("some.deep.nested.object.somewhere") )
    

    Here's a jsfiddle来测试它,特别是它包含一些jQuery,如果你直接修改Object.prototype因为属性变得可枚举而中断 . 这应该适用于第三方库 .

  • 1

    我认为这是一个小小的改进(变成一线):

    alert( test.level1 && test.level1.level2 && test.level1.level2.level3 )
    

    这是有效的,因为&&运算符返回它评估的最终操作数(并且它是短路的) .

  • 1

    This works with all objects and arrays :)

    例如:

    if( obj._has( "something.['deep']['under'][1][0].item" ) ) {
        //do something
    }
    

    这是我对Brian的答案的改进版本

    我使用_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;
    }});
    

    这是fiddle

  • 9

    这是我的看法 - 大多数这些解决方案忽略了嵌套数组的情况,如:

    obj = {
            "l1":"something",
            "l2":[{k:0},{k:1}],
            "l3":{
                "subL":"hello"
            }
        }
    

    我可能想查一下 obj.l2[0].k

    使用下面的功能,你可以做 deeptest('l2[0].k',obj)

    如果对象存在,该函数将返回true,否则返回false

    function deeptest(keyPath, testObj) {
        var obj;
    
        keyPath = keyPath.split('.')
        var cKey = keyPath.shift();
    
        function get(pObj, pKey) {
            var bracketStart, bracketEnd, o;
    
            bracketStart = pKey.indexOf("[");
            if (bracketStart > -1) { //check for nested arrays
                bracketEnd = pKey.indexOf("]");
                var arrIndex = pKey.substr(bracketStart + 1, bracketEnd - bracketStart - 1);
                pKey = pKey.substr(0, bracketStart);
    			var n = pObj[pKey];
                o = n? n[arrIndex] : undefined;
    
            } else {
                o = pObj[pKey];
            }
            return o;
        }
    
        obj = get(testObj, cKey);
        while (obj && keyPath.length) {
            obj = get(obj, keyPath.shift());
        }
        return typeof(obj) !== 'undefined';
    }
    
    var obj = {
        "l1":"level1",
        "arr1":[
            {"k":0},
            {"k":1},
            {"k":2}
        ],
        "sub": {
           	"a":"letter A",
            "b":"letter B"
        }
    };
    console.log("l1: " + deeptest("l1",obj));
    console.log("arr1[0]: " + deeptest("arr1[0]",obj));
    console.log("arr1[1].k: " + deeptest("arr1[1].k",obj));
    console.log("arr1[1].j: " + deeptest("arr1[1].j",obj));
    console.log("arr1[3]: " + deeptest("arr1[3]",obj));
    console.log("arr2: " + deeptest("arr2",obj));
    
  • 141

    现在我们也可以使用 reduce 来遍历嵌套键:

    // @params o<object>
    // @params path<string> expects 'obj.prop1.prop2.prop3'
    // returns: obj[path] value or 'false' if prop doesn't exist
    
    const objPropIfExists = o => path => {
      const levels = path.split('.');
      const res = (levels.length > 0) 
        ? levels.reduce((a, c) => a[c] || 0, o)
        : o[path];
      return (!!res) ? res : false
    }
    
    const obj = {
      name: 'Name',
      sys: { country: 'AU' },
      main: { temp: '34', temp_min: '13' },
      visibility: '35%'
    }
    
    const exists = objPropIfExists(obj)('main.temp')
    const doesntExist = objPropIfExists(obj)('main.temp.foo.bar.baz')
    
    console.log(exists, doesntExist)
    
  • 1

    我想我今天还会添加另一个 . 我为这个解决方案感到自豪的原因是它避免了许多解决方案中使用的嵌套括号,例如Object Wrap (by Oliver Steele)

    (在此示例中,我使用下划线作为占位符变量,但任何变量名称都可以使用)

    //the 'test' object
    var test = {level1: {level2: {level3: 'level3'}}};
    
    let _ = test;
    
    if ((_=_.level1) && (_=_.level2) && (_=_.level3)) {
    
      let level3 = _;
      //do stuff with level3
    
    }
    
    //you could also use 'stacked' if statements. This helps if your object goes very deep. 
    //(formatted without nesting or curly braces except the last one)
    
    let _ = test;
    
    if (_=_.level1)
    if (_=_.level2)
    if (_=_.level3) {
    
       let level3 = _;
       //do stuff with level3
    }
    
    
    //or you can indent:
    if (_=_.level1)
      if (_=_.level2)
        if (_=_.level3) {
    
          let level3 = _;
          //do stuff with level3
    }
    
  • 5

    这是一个函数here on thecodeabode (safeRead),它将以安全的方式做到这一点......即

    safeRead(test, 'level1', 'level2', 'level3');
    

    如果任何属性为null或未定义,则返回空字符串

  • 41

    我编写了自己的函数,它采用了所需的路径,并且具有好的和坏的回调函数 .

    function checkForPathInObject(object, path, callbackGood, callbackBad){
        var pathParts = path.split(".");
        var currentObjectPath = object;
    
        // Test every step to see if it exists in object
        for(var i=0; i<(pathParts.length); i++){
            var currentPathPart = pathParts[i];
            if(!currentObjectPath.hasOwnProperty(pathParts[i])){
                if(callbackBad){
                    callbackBad();
                }
                return false;
            } else {
                currentObjectPath = currentObjectPath[pathParts[i]];
            }
        }
    
        // call full path in callback
        callbackGood();
    }
    

    用法:

    var testObject = {
        level1:{
            level2:{
                level3:{
                }
            }
        }
    };
    
    
    checkForPathInObject(testObject, "level1.level2.level3", function(){alert("good!")}, function(){alert("bad!")}); // good
    
    checkForPathInObject(testObject, "level1.level2.level3.levelNotThere", function(){alert("good!")}, function(){alert("bad!")}); //bad
    
  • 1
    //Just in case is not supported or not included by your framework
    //***************************************************
    Array.prototype.some = function(fn, thisObj) {
      var scope = thisObj || window;
      for ( var i=0, j=this.length; i < j; ++i ) {
        if ( fn.call(scope, this[i], i, this) ) {
          return true;
        }
      }
      return false;
    };
    //****************************************************
    
    function isSet (object, string) {
      if (!object) return false;
      var childs = string.split('.');
      if (childs.length > 0 ) {
        return !childs.some(function (item) {
          if (item in object) {
            object = object[item]; 
            return false;
          } else return true;
        });
      } else if (string in object) { 
        return true;
      } else return false;
    }
    
    var object = {
      data: {
        item: {
          sub_item: {
            bla: {
              here : {
                iam: true
              }
            }
          }
        }
      }
    };
    
    console.log(isSet(object,'data.item')); // true
    console.log(isSet(object,'x')); // false
    console.log(isSet(object,'data.sub_item')); // false
    console.log(isSet(object,'data.item')); // true
    console.log(isSet(object,'data.item.sub_item.bla.here.iam')); // true
    
  • 4

    如果属性存在,我正在寻找要返回的值,所以我通过上面的CMS修改了答案 . 这就是我想出的:

    function getNestedProperty(obj, key) {
      // Get property array from key string
      var properties = key.split(".");
    
      // Iterate through properties, returning undefined if object is null or property doesn't exist
      for (var i = 0; i < properties.length; i++) {
        if (!obj || !obj.hasOwnProperty(properties[i])) {
          return;
        }
        obj = obj[properties[i]];
      }
    
      // Nested property found, so return the value
      return obj;
    }
    
    
    Usage:
    
    getNestedProperty(test, "level1.level2.level3") // "level3"
    getNestedProperty(test, "level1.level2.foo") // undefined
    
  • 2

    我遇到了同样的问题,并希望看看我是否能想出一个自己的解决方案 . 这将接受您要检查为字符串的路径 .

    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 testCases = [
      ["data.Messages[0].Code", true],
      ["data.Messages[1].Code", true],
      ["data.Messages[0]['Code']", true],
      ['data.Messages[0]["Code"]', true],
      ["data[Messages][0]['Code']", false],
      ["data['Messages'][0]['Code']", true]
    ];
    var path = "data.Messages[0].Code";
    var obj = {
      data: {
        Messages: [{
          Code: "0"
        }, {
          Code: "1"
        }]
      }
    }
    
    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 logOutput = [];
      var exists = path.every(key => {
        var currentItem = lastRef[path[steps]];
        if (currentItem) {
          logOutput.push(currentItem);
          lastRef = currentItem;
          steps++;
          return true;
        } else {
          return false;
        }
      });
      console.log(exists, logOutput);
      return exists;
    }
    
    testCases.forEach(testCase => {
      if (checkPathForTruthy(obj, testCase[0]) === testCase[1]) {
        console.log("Passed: " + testCase[0]);
      } else {
        console.log("Failed: " + testCase[0] + " expected " + testCase[1]);
      }
    });
    
  • 313

    轻微编辑为this answer以允许路径中的嵌套数组

    var has = function (obj, key) {
        return key.split(".").every(function (x) {
            if (typeof obj != "object" || obj === null || !x in obj)
                return false;
            if (obj.constructor === Array) 
                obj = obj[0];
            obj = obj[x];
            return true;
        });
    }
    

    检查用户的链接答案:)

相关问题