【问题标题】:Object has-property-deep check in JavaScriptJavaScript 中的对象具有属性深度检查
【发布时间】:2016-01-31 09:27:14
【问题描述】:

假设我们有这个 JavaScript 对象:

var object = {
   innerObject:{
       deepObject:{
           value:'Here am I'
       }
   }
};

我们如何检查value属性是否存在?

我只能看到两种方式:

第一个:

if(object && object.innerObject && object.innerObject.deepObject && object.innerObject.deepObject.value) {
    console.log('We found it!');
}

第二个:

if(object.hasOwnProperty('innerObject') && object.innerObject.hasOwnProperty('deepObject') && object.innerObject.deepObject.hasOwnProperty('value')) {
    console.log('We found it too!');
}

但是有没有办法进行深入检查?比方说,类似:

object['innerObject.deepObject.value']

object.hasOwnProperty('innerObject.deepObject.value')

【问题讨论】:

  • 当然,使用任何支持它的数字库。
  • 您可以轻松编写一个函数,该函数接受这样的字符串,将其拆分为属性名称数组,然后进入循环检查每个属性是否存在。

标签: javascript


【解决方案1】:

您可以使用递归方法来执行此操作。

该方法将迭代(递归)您传入的对象的所有“对象”属性,并在找到包含您传入的属性的对象后立即返回true。如果没有对象包含此类属性,则返回false.

var obj = {
  innerObject: {
    deepObject: {
      value: 'Here am I'
    }
  }
};

function hasOwnDeepProperty(obj, prop) {
  if (typeof obj === 'object' && obj !== null) { // only performs property checks on objects (taking care of the corner case for null as well)
    if (obj.hasOwnProperty(prop)) {              // if this object already contains the property, we are done
      return true;
    }
    for (var p in obj) {                         // otherwise iterate on all the properties of this object.
      if (obj.hasOwnProperty(p) &&               // and as soon as you find the property you are looking for, return true
          hasOwnDeepProperty(obj[p], prop)) { 
        return true;
      }
    }
  }
  return false;                                  
}

console.log(hasOwnDeepProperty(obj, 'value'));   // true
console.log(hasOwnDeepProperty(obj, 'another')); // false

【讨论】:

  • 恐怕这个函数会在任何内部对象中寻找属性,而不是具体的。
  • 哦,我想我误会了你。好吧,Viktor Bahtev's answer 是最好的选择。
  • @nem 很好的广泛可用的解决方案!
  • 是的,也许这不是最好的例子。我必须遍历一些有错误的对象,并且每个对象都具有相同的属性“消息”。您的示例很棒,但不适用于我的情况:)
  • 只要你没有任何循环引用。
【解决方案2】:

这种检查没有内置方法,但您可以轻松实现。创建一个函数,传递一个表示属性路径的字符串,用.分割路径,然后遍历这个路径:

Object.prototype.hasOwnNestedProperty = function(propertyPath) {
  if (!propertyPath)
    return false;

  var properties = propertyPath.split('.');
  var obj = this;

  for (var i = 0; i < properties.length; i++) {
    var prop = properties[i];

    if (!obj || !obj.hasOwnProperty(prop)) {
      return false;
    } else {
      obj = obj[prop];
    }
  }

  return true;
};

// Usage:
var obj = {
  innerObject: {
    deepObject: {
      value: 'Here am I'
    }
  }
}

console.log(obj.hasOwnNestedProperty('innerObject.deepObject.value'));

【讨论】:

    【解决方案3】:

    替代递归函数:

    循环遍历所有对象键。对于任何键,它都会检查它是否是一个对象,如果是,则递归调用自身。

    否则,它会为名称为 propName 的任何键返回一个包含 true、false、false 的数组。

    .reduce 然后通过 or 语句汇总数组。

    function deepCheck(obj,propName) {
      if obj.hasOwnProperty(propName) {             // Performance improvement (thanks to @nem's solution)
        return true;
      }
      return Object.keys(obj)                       // Turns keys of object into array of strings
        .map(prop => {                              // Loop over the array
          if (typeof obj[prop] == 'object') {       // If property is object,
            return deepCheck(obj[prop],propName);   // call recursively
          } else {
            return (prop == propName);              // Return true or false
          }
        })                                          // The result is an array like [false, false, true, false]
        .reduce(function(previousValue, currentValue, index, array) {
          return previousValue || currentValue;
        }                                           // Do an 'or', or comparison of everything in the array.
                                                    // It returns true if at least one value is true.
      )
    }
    
    deepCheck(object,'value'); // === true
    

    PS:nem035's answer 展示了如何提高性能:他的解决方案在第一个找到的“价值”处中断。

    【讨论】:

      【解决方案4】:

      我的方法是使用 try/catch 块。因为我不喜欢在字符串中传递深层属性路径。我是一个喜欢自动补全的懒人:)

      JavaScript 对象在运行时进行评估。因此,如果您在回调函数中返回对象语句,则在调用回调函数之前不会评估该语句。

      所以这个函数只是将回调函数包装在 try catch 语句中。如果它捕获到异常,则返回 false。

      var obj = {
        innerObject: {
          deepObject: {
            value: 'Here am I'
          }
        }
      };
      
      const validate = (cb) => {
        try {
          return cb();
        } catch (e) {
          return false;
        }
      }
      
      
      if (validate(() => obj.innerObject.deepObject.value)) {
       // Is going to work
      }
      
      
      if (validate(() => obj.x.y.z)) {
       // Is not going to work
      }

      在性能方面,很难说哪种方法更好。 在我的测试中,如果对象属性存在并且语句成功,我注意到使用 try/catch 可以比将字符串拆分为键并检查对象中是否存在键快 2 倍 3 倍。

      但如果该属性在某些时候不存在,原型方法返回结果的速度几乎快 7 倍。

      自己看测试:https://jsfiddle.net/yatki/382qoy13/2/

      你也可以查看我这里写的库:https://github.com/yatki/try-to-validate

      【讨论】:

        【解决方案5】:

        试试这个简单易用的解决方案:

        public hasOwnDeepProperty(obj, path)
        {
            for (var i = 0, path = path.split('.'), len = path.length; i < len; i++)
            {
                obj = obj[path[i]];
                if (!obj) return false;
            };
            return true;
        }
        

        【讨论】:

        • 解释一下。
        【解决方案6】:

        如果您正在为 Node.js 编写 JavaScript,那么有一个 assert module with a 'deepEqual' method

        const assert = require('assert');
        assert.deepEqual(testedObject, {
           innerObject:{
              deepObject:{
                  value:'Here am I'
              }
           }
        });
        

        【讨论】:

          【解决方案7】:

          我使用 try-catch:

          var object = {
             innerObject:{
                 deepObject:{
                     value:'Here am I'
                 }
             }
          };
          var object2 = {
            a: 10
          }
          
          let exist = false, exist2 = false;
          
          try {
            exist  = !!object.innerObject.deepObject.value
            exist2 = !!object2.innerObject.deepObject.value
          }
          catch(e) {
          }
          
          console.log(exist);
          console.log(exist2);
          

          【讨论】:

            【解决方案8】:

            我使用递归和快乐流编码策略为此创建了一个非常简单的函数。将它添加到 Object.prototype(使用 enumerate:false !!)以使其可用于所有对象也很好。

            function objectHasOwnNestedProperty(obj, keys)
            {
              if (!obj || typeof obj !== 'object')
              {
                return false;
              }
            
              if(typeof keys === 'string')
              {
                keys = keys.split('.');
              }
            
              if(!Array.isArray(keys))
              {
                return false;
              }
            
              if(keys.length == 0)
              {
                return Object.keys(obj).length > 0;
              }
            
              var first_key = keys.shift();
            
              if(!obj.hasOwnProperty(first_key))
              {
                return false;
              }
            
              if(keys.length == 0)
              {
                return true;
              }
            
              return objectHasOwnNestedProperty(obj[first_key],keys);
            }
            

            Object.defineProperty(Object.prototype, 'hasOwnNestedProperty',
            {
                value: function () { return objectHasOwnNestedProperty(this, ...arguments); },
                enumerable: false
            });
            

            【讨论】:

            • 谁发明了“递归快乐流编码策略”
            猜你喜欢
            • 2017-06-07
            • 2023-03-27
            • 2014-05-08
            • 1970-01-01
            • 2014-01-01
            • 2017-01-09
            • 2012-04-16
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多