簡體   English   中英

JavaScript 中的對象具有屬性深度檢查

[英]Object has-property-deep check in JavaScript

假設我們有這個 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.xyz)) { // 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
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM