JavaScript 中对象具有属性深度检查

问题描述 投票:0回答:9

假设我们有这个 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
9个回答
26
投票

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

.
分割该路径,然后迭代该路径:

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


17
投票

你可以使用递归方法来做到这一点。

该方法将迭代(递归)您传入的对象的所有“对象”属性,并在找到包含您传入的属性的属性后立即返回

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


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 的答案展示了如何提高性能:他的解决方案在第一个找到“值”时就中断了。


3
投票

我的方法是使用 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


1
投票

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

0
投票

尝试这个又好又简单的解决方案:

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

0
投票

如果您正在为 Node.js 编写 JavaScript,那么有一个带有“deepEqual”方法的 assert 模块

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

0
投票

我使用递归和快乐流编码策略为此创建了一个非常简单的函数。将其添加到 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
});

0
投票

解决方案很简单:

console.log((((object||{}).innerObject||{}).deepObject||{}).value)

© www.soinside.com 2019 - 2024. All rights reserved.