Объект JS имеет свойство глубокой проверки

Скажем, у нас есть объект JS:

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

Ответ 1

Для такого типа проверки нет встроенного способа, но вы можете легко реализовать его. Создайте функцию, передайте строку, представляющую путь свойства, разделите путь на . и перейдите по этому пути:

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

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

Ответ 2

Вы можете сделать простой рекурсивный метод для этого.

Метод будет перебирать (рекурсивно) на все свойства объекта объекта, в который вы проходите, и возвращать 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

Альтернативная рекурсивная функция: Петли по всем объектным клавишам, для любого ключа, который он проверяет, является ли он объектом, и если да, то вызывает себя рекурсивно. В противном случае возвращает массив с true, false, false для любой клавиши с именем propName. Затем .reduce свертывает массив через оператор or.

function deepCheck(obj,propName) { 
  if obj.hasOwnProperty(propName) {             // performance improvement (thanks to @nem 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
      } 
    })                                          // result is array like [false, false, true, false]
    .reduce(function(previousValue, currentValue, index, array) { 
      return previousValue || currentValue; 
    }                                           // do an or, or comparison of everything in array
                                                // returns true if at least one value is true
  )
}

deepCheck(object,'value'); // === true

PS: @nem ответ показал, как это может быть более результативным: его решение прерывается при первом найденном "значении".

Ответ 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)) {
 // gonna work
}


if (validate(() => obj.x.y.z)) {
 // not gonna work
}

Ответ 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;
}