JavaScript中的对象深度属性检查

20

假设我们有这个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')

当然可以,使用任何支持该功能的库都可以。 - Dave Newton
你可以很容易地编写一个函数,该函数接受这样的字符串,将其拆分为属性名称数组,并进入循环检查每个属性是否存在。 - Barmar
8个回答

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

您可以编写一个递归方法来实现此操作。

该方法将对您传入的对象的所有'object'属性进行迭代(递归),并在找到包含您传入的属性的属性时立即返回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


很抱歉,这个函数会在任何内部对象中查找属性,而不是特定的对象。 - BadVolt
哦,我想我误解了你的意思。好吧,Viktor Bahtev的答案就是正确的方法。 - nem035
3
@nem 不错的广泛可用解决方案! - wintvelt
是的,也许这不是最好的例子。我必须迭代一些具有错误的对象,每个对象都有相同的属性“message”。你的例子很棒,但不适用于我的情况 :) - BadVolt
BadVolt,wintvelt谢谢!如果有人需要,我仍然会把它留在这里。DanielFlint,好眼力,你是对的。这段代码没有处理循环引用,但可以通过记忆先前访问的路径来解决,但我想现在做这个有点毫无意义了,因为问题已经解决 :) - nem035
显示剩余2条评论

3

替代递归函数:

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

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

然后,.reduce通过一个语句将数组合并。

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的回答展示了如何更高效:他的解决方案在第一个找到的'value.'处中断搜索。


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

需要解释一下。 - Peter Mortensen

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

谁发明了“递归和快乐流编码策略”? - Peter Mortensen

0

如果你正在为Node.js编写JavaScript代码,那么有一个assert模块带有'deepEqual'方法

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接