Object has-property-deep check in JavaScript

后端 未结 8 1933
走了就别回头了
走了就别回头了 2020-12-01 21:16

Let\'s say we have this JavaScript object:

var object = {
   innerObject:{
       deepObject:{
           value:\'Here am I\'
       }
   }
};
<
相关标签:
8条回答
  • 2020-12-01 21:42

    I use 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 讨论(0)
  • 2020-12-01 21:42

    I have created a very simple function for this using the recursive and happy flow coding strategy. It is also nice to add it to the Object.prototype (with enumerate:false!!) in order to have it available for all objects.

    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
    });
    
    0 讨论(0)
  • 2020-12-01 21:49

    My approach would be using try/catch blocks. Because I don't like to pass deep property paths in strings. I'm a lazy guy who likes autocompletion :)

    JavaScript objects are evaluated on runtime. So if you return your object statement in a callback function, that statement is not going to be evaluated until callback function is invoked.

    So this function just wraps the callback function inside a try catch statement. If it catches the exception returns 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
    }

    When it comes to performance, it's hard to say which approach is better. On my tests if the object properties exist and the statement is successful I noticed using try/catch can be 2x 3x times faster than splitting string to keys and checking if keys exist in the object.

    But if the property doesn't exist at some point, prototype approach returns the result almost 7x times faster.

    See the test yourself: https://jsfiddle.net/yatki/382qoy13/2/

    You can also check the library I wrote here: https://github.com/yatki/try-to-validate

    0 讨论(0)
  • 2020-12-01 21:52

    You could make a recursive method to do this.

    The method would iterate (recursively) on all 'object' properties of the object you pass in and return true as soon as it finds one that contains the property you pass in. If no object contains such property, it returns 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

    0 讨论(0)
  • 2020-12-01 21:54

    In case you are writing JavaScript for Node.js, then there is an assert module with a 'deepEqual' method:

    const assert = require('assert');
    assert.deepEqual(testedObject, {
       innerObject:{
          deepObject:{
              value:'Here am I'
          }
       }
    });
    
    0 讨论(0)
  • 2020-12-01 21:55

    Alternative recursive function:

    Loops over all object keys. For any key it checks if it is an object, and if so, calls itself recursively.

    Otherwise, it returns an array with true, false, false for any key with the name propName.

    The .reduce then rolls up the array through an or statement.

    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's answer showed how it could be more performant: his solution breaks off at the first found 'value.'

    0 讨论(0)
提交回复
热议问题