假设我们有这个 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')
这种检查没有内置方法,但您可以轻松实现。创建一个函数,传递一个表示属性路径的字符串,用
.
分割该路径,然后迭代该路径:
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'));
你可以使用递归方法来做到这一点。
该方法将迭代(递归)您传入的对象的所有“对象”属性,并在找到包含您传入的属性的属性后立即返回
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
替代递归函数:
循环所有对象键。对于任何键,它都会检查它是否是一个对象,如果是,则递归地调用自身。
否则,对于任何名称为
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 的答案展示了如何提高性能:他的解决方案在第一个找到“值”时就中断了。
我的方法是使用 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
我使用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);
尝试这个又好又简单的解决方案:
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;
}
如果您正在为 Node.js 编写 JavaScript,那么有一个带有“deepEqual”方法的 assert 模块:
const assert = require('assert');
assert.deepEqual(testedObject, {
innerObject:{
deepObject:{
value:'Here am I'
}
}
});
我使用递归和快乐流编码策略为此创建了一个非常简单的函数。将其添加到 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
});
解决方案很简单:
console.log((((object||{}).innerObject||{}).deepObject||{}).value)