代码之家  ›  专栏  ›  技术社区  ›  user113716

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

  •  517
  • user113716  · 技术社区  · 15 年前

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

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

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

    49 回复  |  直到 6 年前
        1
  •  331
  •   Esailija    10 年前

    如果你不想 TypeError ,因为如果其中一个成员 null undefined ,并且您尝试访问成员时,将引发异常。

    你可以简单地 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
  •  311
  •   Ben Creasy Dgon Tarn    8 年前

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

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

    事实上,整篇文章都在讨论如何使用javascript实现这一点。他决定用上面的语法(一旦习惯了就不难理解)作为一种习惯用法。

        3
  •  218
  •   mikemaccana    8 年前

    更新

    看起来像洛达斯 has added _.get 满足所有嵌套属性的获取需求。

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

    https://lodash.com/docs#get


    上一个答案

    lodash 用户可以享受 lodash.contrib 哪个有 couple methods that mitigate this problem .

    获取路径

    签名: _.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
    
        4
  •  162
  •   Necreaux    7 年前

    我已经做了 performance tests (谢谢) cdMinix 对于添加lodash)对这个问题提出的一些建议,结果如下。

    免责声明1 将字符串转换为引用是不必要的元编程,可能最好避免。首先不要忘记你的推荐人。 Read more from this answer to a similar question .

    免责声明2 我们这里讨论的是每毫秒数百万次的操作。在大多数用例中,这些都不太可能起到很大的作用。选择最有意义的一个,知道每一个的局限性。对我来说,我会选择 reduce 为了方便。

    Object Wrap (by Oliver Steele) 最快34%

    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%”最慢

    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);
    
        5
  •  41
  •   kennebec    10 年前

    如果将名称处理为字符串,则可以在任何深度读取对象属性: '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 如果任何部分 未定义 .

        6
  •  25
  •   Gajus    9 年前
    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
    
        7
  •  18
  •   user187291    15 年前

    怎么样

    try {
       alert(test.level1.level2.level3)
    } catch(e) {
     ...whatever
    
    }
    
        8
  •  12
  •   Frank N dqthe    7 年前

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

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

    艾尔见 Codepen with full test coverage

        9
  •  8
  •   jrode    11 年前

    我尝试了递归方法:

    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';
    
        10
  •  8
  •   Goran.it    7 年前

    您还可以将tc39可选链接建议与babel 7一起使用。- tc39-proposal-optional-chaining

    代码如下所示:

      const test = test?.level1?.level2?.level3;
      if (test) alert(test);
    
        11
  •  7
  •   VeganHunter    8 年前

    我认为下面的脚本提供了更可读的表示。

    声明函数:

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

    然后像这样使用:

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

    我称之为“悲伤的小丑技巧”,因为它使用了符号o(


    编辑:

    这是一个版本 打字稿

    它在编译时提供类型检查(如果使用像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")
    
        12
  •  5
  •   Michiel Kusum Kushwaha    7 年前

    我没有看到有人使用 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 锁链是…这可能会阻碍你所知道的一切…但是,使用promise中的相同代理技术,您实际上可以在不编写单个函数的情况下测试深嵌套复杂路径的存在性

    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
        13
  •  4
  •   jfriend00    13 年前

    一个简单的方法是:

    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)的情况。

        14
  •  4
  •   Community CDub    8 年前

    基于 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 );
    }
    
        15
  •  3
  •   mikemaccana    13 年前

    一个简短的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
    
        16
  •  2
  •   Anand Sunderraman    12 年前

    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;
        }
    
        17
  •  2
  •   Community CDub    8 年前

    以下选项从 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
    
        18
  •  2
  •   Brian Sidebotham    10 年前

    我知道这个问题已经过时了,但我想通过向所有对象添加这个来提供一个扩展。我知道人们不喜欢使用对象原型来扩展对象功能,但我发现没有比这样做更容易的了。另外,现在允许 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,就会中断,因为属性变成了可枚举的。这对第三方库应该很有用。

        19
  •  2
  •   Julius Musseau    10 年前

    我认为这是一个轻微的改进(变成一个1行):

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

    这是因为&运算符返回其计算的最后一个操作数(并且它短路)。

        20
  •  2
  •   Noah Stahl    10 年前

    如果属性存在,我正在寻找要返回的值,因此我修改了上面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
        21
  •  2
  •   adutu    9 年前

    这适用于所有对象和数组:)

    前任:

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

    这是我对布莱恩答案的改进版本

    我用过 作为属性名,因为它可能与现有的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

        22
  •  2
  •   Seth    8 年前

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

        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));
        23
  •  2
  •   Egor Stambakio    8 年前

    现在我们也可以用 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)
        24
  •  2
  •   kubakoz    7 年前

    我想再加一个我今天想到的。我为这个解决方案感到自豪的原因是,它避免了在许多解决方案(如 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
    }
        25
  •  2
  •   V. Sambor    7 年前

    我创建了一个小函数来安全地获取嵌套对象属性。

    function getValue(object, path, fallback, fallbackOnFalsy) {
        if (!object || !path) {
            return fallback;
        }
    
        // Reduces object properties to the deepest property in the path argument.
        return path.split('.').reduce((object, property) => {
           if (object && typeof object !== 'string' && object.hasOwnProperty(property)) {
                // The property is found but it may be falsy.
                // If fallback is active for falsy values, the fallback is returned, otherwise the property value.
                return !object[property] && fallbackOnFalsy ? fallback : object[property];
            } else {
                // Returns the fallback if current chain link does not exist or it does not contain the property.
                return fallback;
            }
        }, object);
    }
    

    或者是一个简单但有点不可读的版本:

    function getValue(o, path, fb, fbFalsy) {
       if(!o || !path) return fb;
       return path.split('.').reduce((o, p) => o && typeof o !== 'string' && o.hasOwnProperty(p) ? !o[p] && fbFalsy ? fb : o[p] : fb, o);
    }
    

    或者更短,但不退却错误的旗帜:

    function getValue(o, path, fb) {
       if(!o || !path) return fb;
       return path.split('.').reduce((o, p) => o && typeof o !== 'string' && o.hasOwnProperty(p) ? o[p] : fb, o);
    }
    

    我有以下测试:

    const obj = {
        c: {
            a: 2,
            b: {
                c: [1, 2, 3, {a: 15, b: 10}, 15]
            },
            c: undefined,
            d: null
        },
        d: ''
    }
    

    下面是一些测试:

    // null
    console.log(getValue(obj, 'c.d', 'fallback'));
    
    // array
    console.log(getValue(obj, 'c.b.c', 'fallback'));
    
    // array index 2
    console.log(getValue(obj, 'c.b.c.2', 'fallback'));
    
    // no index => fallback
    console.log(getValue(obj, 'c.b.c.10', 'fallback'));
    

    要查看包含文档和我尝试过的测试的所有代码,可以查看我的github要点: https://gist.github.com/vsambor/3df9ad75ff3de489bbcb7b8c60beebf4#file-javascriptgetnestedvalues-js

        26
  •  1
  •   Ben    12 年前

    有一个功能 here on thecodeabode (safeRead) 会以一种安全的方式…即

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

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

        27
  •  1
  •   Community CDub    8 年前

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

    // Supposing that our property is at first.second.third.property:
    var property = (((typeof first !== 'undefined' ? first : {}).second || {}).third || {}).property;
    
        28
  •  1
  •   Stephane LaFlèche    10 年前

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

    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
    
        29
  •  1
  •   alejandro    10 年前
    //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
    
        30
  •  1
  •   Jonathan    8 年前

    我也有同样的问题,想看看能不能想出自己的解决办法。它接受要检查的路径作为字符串。

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