【问题标题】:Test for existence of nested JavaScript object key测试嵌套 JavaScript 对象键是否存在
【发布时间】:2011-02-07 12:41:53
【问题描述】:

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

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

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

【问题讨论】:

标签: javascript object properties nested


【解决方案1】:

怎么样

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

}

【讨论】:

  • 我不认为 try/catch 是测试对象是否存在的好方法:try/catch 旨在处理异常,而不是正常情况,例如此处的测试。我认为 (typeof foo == "undefined") 在每一步都更好——一般来说,如果您使用如此深层次的嵌套属性,可能需要进行一些重构。此外,如果抛出异常,try/catch 将导致 Firebug 中断(以及在任何打开错误中断的浏览器中)。
  • 我对此投票,因为如果您使用其他解决方案,浏览器会检查两次是否存在。假设您想调用“a.c.b = 2”。浏览器必须在修改值之前检查是否存在(否则会被操作系统捕获到内存错误)。
  • 问题仍然存在:对于浏览器设置 try catch 或调用 hasOwnProperty() n 次,女巫更快?
  • 为什么又变坏了?这对我来说看起来最干净。
  • 我想说:如果您期望该属性存在,那么可以将其包装到 try 块中。如果它不存在,则它是一个错误。但是,如果您只是懒惰并在属性不存在的情况下将常规代码放入 catch 块中,则 try/catch 被滥用。这里需要 if/else 或类似的东西。
【解决方案2】:

如果您不想要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

ES6 更新:

这里是原始函数的一个更短的版本,使用 ES6 特性和递归(它也是proper tail call 形式):

function checkNested(obj, level,  ...rest) {
  if (obj === undefined) return false
  if (rest.length == 0 && obj.hasOwnProperty(level)) return true
  return checkNested(obj[level], ...rest)
}

但是,如果你想获取嵌套属性的值而不只是检查它的存在,这里有一个简单的单行函数:

function getNested(obj, ...args) {
  return args.reduce((obj, level) => obj && obj[level], obj)
}

const test = { level1:{ level2:{ level3:'level3'} } };
console.log(getNested(test, 'level1', 'level2', 'level3')); // 'level3'
console.log(getNested(test, 'level1', 'level2', 'level3', 'length')); // 6
console.log(getNested(test, 'level1', 'level2', 'foo')); // undefined
console.log(getNested(test, 'a', 'b')); // undefined

上面的函数可以让你获取嵌套属性的值,否则会返回undefined

2019 年 10 月 17 日更新:

optional chaining proposalECMAScript committee process 上达到了第 3 阶段,这将允许您通过使用令牌 ?.(新的可选链接运算符)安全地访问深度嵌套的属性:: p>

const value = obj?.level1?.level2?.level3 

如果访问的任何级别是 nullundefined,则表达式将自行解析为 undefined

该提案还允许您安全地处理方法调用:

obj?.level1?.method();

如果objobj.level1obj.level1.methodnullundefined,上述表达式将产生undefined,否则将调用该函数。

您可以使用 optional chaining plugin 开始使用 Babel 使用此功能。

由于Babel 7.8.0,默认支持ES2020

在 Babel REPL 上检查 this example

??更新:2019 年 12 月??

在 2019 年 12 月的 TC39 委员会会议上,可选链提案终于 reached Stage 4。这意味着此功能将成为 ECMAScript 2020 标准的一部分。

【讨论】:

  • arguments 实际上不是一个数组。 Array.prototype.slice.call(arguments) 将其转换为正式数组。 Learn
  • 这将是 a lot more efficient 执行 var obj = arguments[0]; 并从 var i = 1 开始,而不是复制 arguments 对象
  • 为了紧缩的缘故,我将一个带有 try/catch 的版本放在一起,这并不奇怪 - 性能很糟糕(出于某种原因在 Safari 中除外)。下面有一些非常高效的答案,以及 Claudiu 的修改,它也比选定的答案更高效。在此处查看 jsperf jsperf.com/check-if-deep-property-exists-with-willnotthrow
  • 在 ES6 中,args 变量声明可以被移除,...args 可以用作checkNested 方法的第二个参数。 developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
  • 这是一个非常不可维护的。如果任何属性键更改(它们会更改),项目中的所有开发人员都必须“字符串搜索”整个代码库。这并不是问题的真正解决方案,因为它引入了一个更大的问题
【解决方案3】:

如果您像处理字符串一样处理名称,则可以读取任意深度的对象属性:'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

【讨论】:

【解决方案4】:

我自动化了这个过程

if(isset(object,["prop1","prop2"])){
// YES!

}

function isset(object, props){
    var dump;
    try {
        for(var x in props){
            if(x == 0) {
                dump = object[props[x]];
                return;
            }
            dump = dump[props[x]];
        }
    } catch(e) {
        return false;
    }

    return true;
}

【讨论】:

  • 有几点需要注意。您正在对数组执行for/in,不建议这样做。这实际上是针对对象的。不能保证执行顺序是一致的。另外,如果您要遍历属性,我可能不会使用try/catch。我认为采用if( props[x] in object ) 方法或if( object.hasOwnProperty(props[x]) ) 如果您不想包含prototype 属性,您将获得更好的性能。我的情况是,我只对最深的财产感兴趣。这就是我选择try/catch的原因。
  • 如果你仔细观察,我使用转储变量进入对象级别,我没有停留在级别 1
  • 但是你说得对/in,我的心碎了:/,它也慢了
【解决方案5】:

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

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

事实上,整篇文章都是在讨论如何在 javascript 中做到这一点。他决定使用上述语法(一旦习惯了就不会那么难读)作为习语。

【讨论】:

  • @wared 我认为有趣的是它的简洁程度。链接帖子中详细讨论了性能特征。是的,它总是进行所有测试,但它避免创建临时变量,如果你想防止每次创建新的空对象的开销,你可以将 {} 别名为 var。在 99% 的情况下,我认为速度无关紧要,而且在这种情况下,分析是无可替代的。
  • @MuhammadUmer 不,(test || {}) 的重点是,如果未定义测试,那么您正在执行({}.level1 || {})。当然,{}.level1 是未定义的,所以这意味着你正在做{}.level2,等等。
  • @JoshuaTaylor:我认为他的意思是如果test 没有声明,就会出现ReferenceError,但这不是问题,因为如果没有声明,就会出现一个要修复的错误,所以这个错误是一件好事。
  • 你说“这不是那么难阅读一次习惯。嗯,这些是你知道已经是一团糟的迹象。那为什么建议这个解决方案?它容易出现拼写错误,并且绝对没有可读性。看看吧!如果我不得不写出丑陋的一行,它也应该是可读的;所以我会坚持使用if(test.level1 &amp;&amp; test.level1.level2 &amp;&amp; test.level1.level2.level3)
  • 除非我遗漏了什么,否则这不适用于可能是错误的布尔最终属性......遗憾的是。否则我喜欢这个成语。
【解决方案6】:

一个简单的方法是这样的:

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

【讨论】:

    【解决方案7】:

    @CMS 出色答案的更短的 ES5 版本:

    // 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
    

    【讨论】:

    • 唯一的问题是如果有多个级别的未定义键,那么你会得到一个 TypeError,例如checkObjHasKeys(test, ['level1', 'level2', 'asdf', 'asdf']);
    • 比较合适的方法是every,其值可以直接返回。
    • 也许将success = false; 更改为return false。一旦你知道它坏了,你就应该退出,一旦它为空或未定义,就没有更深的存在了。这将防止更深嵌套项的错误,因为它们显然也不存在。
    【解决方案8】:

    另一个 ES5 解决方案:

    function hasProperties(object, properties) {
        return !properties.some(function(property){
            if (!object.hasOwnProperty(property)) {
                return true;
            }
            object = object[property];
            return false;
        });
    }
    

    【讨论】:

      【解决方案9】:

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

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

      【讨论】:

      • 我认为问题是关于验证属性的存在,而不是在对象的任何一个不同的子对象中搜索它。如果调用urfunc(a,'c')a.b.ca.e.c 都将返回值。像if(exists(a.b.c)) 这样的功能是理想的
      【解决方案10】:

      我长期使用的解决方案(不幸的是使用字符串,找不到更好的解决方案)

      function get_if_exist(str){
          try{return eval(str)}
          catch(e){return undefined}
      }
      
      // way to use
      if(get_if_exist('test.level1.level2.level3')) {
          alert(test.level1.level2.level3);
      }
      
      // or simply 
      alert(get_if_exist('test.level1.level2.level3'));
      

      编辑:仅当对象“测试”具有全局范围/范围时才有效。 否则你必须做类似的事情:

      // i think it's the most beautiful code I have ever write :p
      function get_if_exist(obj){
          return arguments.length==1 || (obj[arguments[1]] && get_if_exist.apply(this,[obj[arguments[1]]].concat([].slice.call(arguments,2))));
      }
      
      alert(get_if_exist(test,'level1','level2','level3'));
      

      编辑最终版本以允许两种调用方法:

      function get_if_exist(obj){
          var a=arguments, b=a.callee; // replace a.callee by the function name you choose because callee is depreceate, in this case : get_if_exist
          // version 1 calling the version 2
          if(a[1] && ~a[1].indexOf('.')) 
              return b.apply(this,[obj].concat(a[1].split('.')));
          // version 2
          return a.length==1 ? a[0] : (obj[a[1]] && b.apply(this,[obj[a[1]]].concat([].slice.call(a,2))));
      }
      
      // method 1
      get_if_exist(test,'level1.level2.level3');
      
      
      // method 2
      get_if_exist(test,'level1','level2','level3');
      

      【讨论】:

        【解决方案11】:

        有一个函数here on thecodeabode (safeRead) 可以安全地执行此操作...即

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

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

        【讨论】:

        • 我有点喜欢这种带有模板的方法,因为如果没有设置它会返回一个空字符串
        【解决方案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;
            }
        

        【讨论】:

          【解决方案13】:

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

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

          【讨论】:

            【解决方案14】:

            另一种选择(接近this answer):

            function resolve(root, path){
                try {
                    return (new Function(
                        'root', 'return root.' + path + ';'
                    ))(root);
                } catch (e) {}
            }
            
            var tree = { level1: [{ key: 'value' }] };
            resolve(tree, 'level1[0].key'); // "value"
            resolve(tree, 'level1[1].key'); // undefined
            

            更多信息:https://stackoverflow.com/a/18381564/1636522

            【讨论】:

              【解决方案15】:

              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
              

              【讨论】:

                【解决方案16】:

                我尝试了递归方法:

                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】:

                  更新

                  看起来像 lodash 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
                  

                  【讨论】:

                  • Lodash 确实需要一个 _.isPathDefined(obj, pathString) 方法。
                  • @MatthewPayne 也许会很好,但实际上没有必要。你可以很容易地自己做function isPathDefined(object, path) { return typeof _.getPath(object, path) !== 'undefined'; }
                  • Lodash 本身也有同样的功能:_.get(countries, 'greece.sparta.playwright', 'default'); // → 'default' _.has(countries, 'greece.spart.playwright') // → false
                  • 更好的是_.result
                  • 如果您需要确定多个不同的路径,请考虑:var url = _.get(e, 'currentTarget.myurl', null) || _.get(e, 'currentTarget.attributes.myurl.nodeValue', null) || null
                  【解决方案18】:

                  另一个版本:

                  function nestedPropertyExists(obj, props) {
                      var prop = props.shift();
                      return prop === undefined
                          ? true
                          : obj.hasOwnProperty(prop) ? nestedPropertyExists(obj[prop], props) : false;
                  }
                  
                  nestedPropertyExists({a:{b:{c:1}}}, ['a','b','c']); // returns true
                  nestedPropertyExists({a:{b:{c:1}}}, ['a','b','c','d']); // returns false
                  

                  【讨论】:

                    【解决方案19】:
                    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
                    

                    【讨论】:

                    • 我认为try-catch 方法是最好的答案。查询对象的类型与假设 API 存在并且如果不存在则相应地失败之间存在哲学差异。后者更适合松散类型的语言。见stackoverflow.com/a/408305/2419669try-catch 方法也比if (foo &amp;&amp; foo.bar &amp;&amp; foo.bar.baz &amp;&amp; foo.bar.baz.qux) { ... } 清晰得多。
                    【解决方案20】:

                    我写了一个library called l33teral to help test for nested properties。你可以这样使用它:

                    var myObj = {/*...*/};
                    var hasNestedProperties = leet(myObj).probe('prop1.prop2.prop3');
                    

                    我也很喜欢这里的 ES5/6 解决方案。

                    【讨论】:

                      【解决方案21】:
                      function isIn(string, object){
                          var arr = string.split(".");
                          var notFound = true;
                          var length = arr.length;
                          for (var i = 0; i < length; i++){
                              var key = arr[i];
                              if (!object.hasOwnProperty(key)){
                                  notFound = false;
                                  break;
                              }
                              if ((i + length) <= length){
                                  object = object[key];
                              }
                          }
                          return notFound;
                      }
                      var musicCollection = {
                          hasslehoff: {
                              greatestHits : true
                          }
                      };
                      console.log(isIn("hasslehoff.greatestHits", musicCollection));
                      console.log(isIn("hasslehoff.worseHits", musicCollection));
                      

                      这里是我的基于字符串的分隔符版本。

                      【讨论】:

                        【解决方案22】:

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

                        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
                        

                        【讨论】:

                        • 我公平地感谢您将您的代码改编为my answer的灵感
                        【解决方案23】:

                        我知道这个问题很老,但我想通过将它添加到所有对象来提供扩展。我知道人们倾向于不赞成使用对象原型来扩展对象功能,但我没有发现比这样做更容易的事情了。另外,现在可以使用 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 会因为属性变得可枚举而中断。这应该适用于 3rd 方库。

                        【讨论】:

                          【解决方案24】:
                          //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
                          

                          【讨论】:

                            【解决方案25】:

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

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

                            之所以有效,是因为 && 运算符返回它评估的最终操作数(并且它会短路)。

                            【讨论】:

                            • 你从字面上复制了他们所说的他们通常做的事情,并想避免......
                            【解决方案26】:

                            如果属性存在,我正在寻找要返回的值,所以我修改了上面 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

                            【讨论】:

                              【解决方案27】:

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

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

                              【讨论】:

                                【解决方案28】:

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

                                例如:

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

                                这是我对布赖恩回答的改进版

                                我使用 _has 作为属性名称,因为它可能与现有的 has 属性冲突(例如:地图)

                                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

                                【讨论】:

                                  【解决方案29】:

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

                                  声明一个函数:

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

                                  然后像这样使用它:

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

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


                                  编辑:

                                  这里是 TypeScript 的版本

                                  它在编译时提供类型检查(如果您使用 Visual Studio 之类的工具,还提供智能感知)

                                  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
                                  

                                  但这一次智能感知有效!

                                  另外,你可以设置一个默认值:

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

                                  【讨论】:

                                  • °0o &lt;°(())))&gt;&lt;
                                  • 我喜欢这个,因为它很诚实,当你不知道你的Object 类型时,它会在你脸上抛出一个“未定义”。 +1。
                                  • 只要你把声明放在括号里你也可以称之为快乐小丑技术(o
                                  • 谢谢 Sventies。我喜欢你的评论。这是一个非常好的视角——这样的条件主要用于“ifs”并且总是用外部括号括起来。所以,是的,它确实是一个快乐的小丑:)))
                                  • 你真的需要爱上括号才能找到这个......
                                  【解决方案30】:

                                  基于@Stephane LaFlèche's answer,我想出了我的脚本的替代版本。

                                  Demo on JSFiddle

                                  var obj = {"a":{"b":{"c":"Hello World"}},"resTest":"potato","success":"This path exists"};
                                  checkForPathInObject = function(object,path,value) {
                                          var pathParts   = path.split("."),
                                              result      = false;
                                          // Check if required parameters are set; if not, return false
                                          if(!object || typeof object == 'undefined' || !path || typeof path != 'string')
                                              return false;
                                          /* Loop through object keys to find a way to the path or check for value
                                           * If the property does not exist, set result to false
                                           * If the property is an object, update @object
                                           * Otherwise, update result */
                                          for(var i=0;i<pathParts.length;i++){
                                              var currentPathPart = pathParts[i];
                                              if(!object.hasOwnProperty( currentPathPart )) {
                                                  result = false;
                                              } else if (object[ currentPathPart ] && path == pathParts[i]) {
                                                  result = pathParts[i];
                                                  break;
                                              } else if(typeof object[ currentPathPart ] == 'object') {
                                                  object = object[ currentPathPart ];
                                              } else {
                                                  result = object[ currentPathPart ];
                                              }
                                          }
                                          /* */
                                          if(typeof value != 'undefined' && value == result)
                                              return true;
                                          return result;
                                  };
                                  // Uncomment the lines below to test the script
                                  // alert( checkForPathInObject(obj,'a.b.c') ); // Results "Hello World"
                                  // alert( checkForPathInObject(obj,'a.success') ); // Returns false
                                  // alert( checkForPathInObject(obj,'resTest', 'potato') ); // Returns true
                                  

                                  【讨论】:

                                    猜你喜欢
                                    • 1970-01-01
                                    相关资源
                                    最近更新 更多