Проверка существования вложенного ключа объекта JavaScript - PullRequest
553 голосов
/ 13 апреля 2010

Если у меня есть ссылка на объект:

var test = {};

, который потенциально (но не сразу) будет иметь вложенные объекты, что-то вроде:

{level1: {level2: {level3: "level3"}}};

Как лучше всегопроверить наличие свойства в глубоко вложенных объектах?

alert(test.level1); дает undefined, но alert(test.level1.level2.level3); не удается.

В настоящее время я делаю что-то вроде этого:

if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
    alert(test.level1.level2.level3);
}

но мне было интересно, есть ли лучший способ.

Ответы [ 53 ]

1 голос
/ 26 июля 2017

Я думал, что добавлю еще один, который я придумал сегодня. Причина, по которой я горжусь этим решением, заключается в том, что оно избегает вложенных скобок, которые используются во многих решениях, таких как Object Wrap (by Oliver Steele) :

(в этом примере я использую подчеркивание в качестве переменной-заполнителя, но подойдет любое имя переменной)

//the 'test' object
var test = {level1: {level2: {level3: 'level3'}}};

let _ = test;

if ((_=_.level1) && (_=_.level2) && (_=_.level3)) {

  let level3 = _;
  //do stuff with level3

}

//you could also use 'stacked' if statements. This helps if your object goes very deep. 
//(formatted without nesting or curly braces except the last one)

let _ = test;

if (_=_.level1)
if (_=_.level2)
if (_=_.level3) {

   let level3 = _;
   //do stuff with level3
}


//or you can indent:
if (_=_.level1)
  if (_=_.level2)
    if (_=_.level3) {

      let level3 = _;
      //do stuff with level3
}
1 голос
/ 22 июня 2017

Небольшое редактирование этого ответа , чтобы разрешить вложенные массивы в пути

var has = function (obj, key) {
    return key.split(".").every(function (x) {
        if (typeof obj != "object" || obj === null || !x in obj)
            return false;
        if (obj.constructor === Array) 
            obj = obj[0];
        obj = obj[x];
        return true;
    });
}

Проверьте связанный ответ для использования:)

1 голос
/ 24 сентября 2018

Ну, нет действительно хорошего ответа для однострочных для использования в HTML-шаблонах, поэтому я сделал один, используя ES6 Proxies . Вы просто передаете объект или значение в функцию «traverse» и делаете столько вложенных вызовов, сколько хотите, закрывая их вызовом функции, который вернет значение или запасное значение. Использование:

const testObject = { 
  deep: { 
    nested: { 
      obj: { 
        closure: () => { return "closure" },
        number: 9,
        boolean: true,
        array: [1, 2, { foo: { bar: true } }]
      } 
    }
  }
}

traverse(testObject).deep() 
// {nested: {…}}

traverse(testObject).non.existent() 
// undefined

traverse(testObject).deep.nested.obj.closure()() 
// closure

traverse(testObject).deep.nested.obj.array[5]('fallback')
// fallback

traverse(testObject).deep.nested.obj.array[2]()
// {foo: {…}}

traverse(testObject).deep.nested.obj.array[2].foo.bar()
// true

traverse(testObject).deep.nested.obj.array[2].foo.bar[4]('fallback')
// fallback

traverse(testObject).completely.wrong[3].call().WILL_THROW()
// Uncaught TypeError: Cannot read property 'WILL_THROW' of undefined

Сама функция:

const traverse = (input) => {
    // unique empty object
    const unset = new Object();
    // we need wrapper to ensure we have access to the same unique empty object
    const closure = (input) => {
        // wrap each input into this
        const handler = new Function();
        handler.input = input;    
        // return wrappers proxy 
        return new Proxy(handler, {
            // keep traversing
            get: (target, name) => {
                // if undefined supplied as initial input
                if (!target.input) {
                    return closure(unset);
                }
                // otherwise
                if (target.input[name] !== undefined) {
                    // input has that property
                    return closure(target.input[name]);
                } else {
                    return closure(unset);
                }
            },
            // result with fallback
            apply: (target, context, args) => {
                return handler.input === unset ? 
                    args[0] : handler.input;
            }
        })
    }
    return closure(input);    
}
0 голосов
/ 25 января 2013

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

/**
 * Performs a deep search looking for the existence of a property in a 
 * nested object. Supports namespaced search: Passing a string with
 * a parent sub-object where the property key may exist speeds up
 * search, for instance: Say you have a nested object and you know for 
 * certain the property/literal you're looking for is within a certain
 * sub-object, you can speed the search up by passing "level2Obj.targetProp"
 * @param {object} obj Object to search
 * @param {object} key Key to search for
 * @return {*} Returns the value (if any) located at the key
 */
var getPropByKey = function( obj, key ) {
    var ret = false, ns = key.split("."),
        args = arguments,
        alen = args.length;

    // Search starting with provided namespace
    if ( ns.length > 1 ) {
        obj = (libName).getPropByKey( obj, ns[0] );
        key = ns[1];
    }

    // Look for a property in the object
    if ( key in obj ) {
        return obj[key];
    } else {
        for ( var o in obj ) {
            if ( (libName).isPlainObject( obj[o] ) ) {
                ret = (libName).getPropByKey( obj[o], key );
                if ( ret === 0 || ret === undefined || ret ) {
                    return ret;
                }
            }
        }
    }

    return false;
}
0 голосов
/ 20 июня 2014

Еще одна версия:

function nestedPropertyExists(obj, props) {
    var prop = props.shift();
    return prop === undefined
        ? true
        : obj.hasOwnProperty(prop) ? nestedPropertyExists(obj[prop], props) : false;
}

nestedPropertyExists({a:{b:{c:1}}}, ['a','b','c']); // returns true
nestedPropertyExists({a:{b:{c:1}}}, ['a','b','c','d']); // returns false
0 голосов
/ 17 августа 2017

Другим способом решения этой проблемы является, например, наличие следующего объекта:

var x = {
    a: {
        b: 3
    }
};

затем я добавил следующую функцию к этому объекту:

x.getKey = function(k){
        var r ;
        try {
            r = eval('typeof this.'+k+' !== "undefined"');
        }catch(e){
            r = false;
        }
        if(r !== false){
            return eval('this.'+k);
        }else{
            console.error('Missing key: \''+k+'\'');
            return '';
        }
    };

тогда вы можете проверить:

x.getKey('a.b');

Если значение не определено, функция возвращает "" (пустую строку), иначе возвращает существующее значение.

Пожалуйста, рассмотрите также другое более сложное решение, проверяющее ссылку: Объект JS имеет глубокую проверку свойства

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

P.S .: Существует также рекурсивная версия.

0 голосов
/ 26 июня 2017

CMS решение отлично работает, но использование / синтаксис может быть более удобным. Я предлагаю следующее

var checkNested = function(obj, structure) {

  var args = structure.split(".");

  for (var i = 0; i < args.length; i++) {
    if (!obj || !obj.hasOwnProperty(args[i])) {
      return false;
    }
    obj = obj[args[i]];
  }
  return true;
};

Вы можете просто использовать объектную нотацию, используя точку вместо предоставления нескольких аргументов

var test = {level1:{level2:{level3:'level3'}} };

checkNested(test, 'level1.level2.level3'); // true
checkNested(test, 'level1.level2.foo'); // false
0 голосов
/ 02 июня 2016

Основываясь на ответе @ Stephane LaFlèche , я придумал альтернативную версию сценария.

Демонстрация на JSFiddle

var obj = {"a":{"b":{"c":"Hello World"}},"resTest":"potato","success":"This path exists"};
checkForPathInObject = function(object,path,value) {
        var pathParts   = path.split("."),
            result      = false;
        // Check if required parameters are set; if not, return false
        if(!object || typeof object == 'undefined' || !path || typeof path != 'string')
            return false;
        /* Loop through object keys to find a way to the path or check for value
         * If the property does not exist, set result to false
         * If the property is an object, update @object
         * Otherwise, update result */
        for(var i=0;i<pathParts.length;i++){
            var currentPathPart = pathParts[i];
            if(!object.hasOwnProperty( currentPathPart )) {
                result = false;
            } else if (object[ currentPathPart ] && path == pathParts[i]) {
                result = pathParts[i];
                break;
            } else if(typeof object[ currentPathPart ] == 'object') {
                object = object[ currentPathPart ];
            } else {
                result = object[ currentPathPart ];
            }
        }
        /* */
        if(typeof value != 'undefined' && value == result)
            return true;
        return result;
};
// Uncomment the lines below to test the script
// alert( checkForPathInObject(obj,'a.b.c') ); // Results "Hello World"
// alert( checkForPathInObject(obj,'a.success') ); // Returns false
// alert( checkForPathInObject(obj,'resTest', 'potato') ); // Returns true
0 голосов
/ 10 мая 2019

Если вы используете AngularJ, вы можете использовать сервис $ parse , чтобы проверить, существует ли свойство глубокого объекта, например:

if( $parse('model.data.items')(vm) ) {
    vm.model.data.items.push('whatever');
}

чтобы избежать таких утверждений:

if(vm.model && vm.model.data && vm.model.data.items) {
    ....
}

не забудьте добавить сервис $ parse в ваш контроллер

для получения дополнительной информации: https://glebbahmutov.com/blog/angularjs-parse-hacks/

0 голосов
/ 18 марта 2019
getValue (o, key1, key2, key3, key4, key5) {
    try {
      return o[key1][key2][key3][key4][key5]
    } catch (e) {
      return null
    }
}
...