2015-10-30 20 views
7

Diciamo che abbiamo JS oggetto:JS oggetto ha proprietà di controllo profondo

var object = { 
    innerObject:{ 
     deepObject:{ 
      value:'Here am I' 
     } 
    } 
}; 

Come possiamo controllare se value proprietà esiste? Vedo solo due modi:

primo:

if(object && object.innerObject && object.innerObject.deepObject && object.innerObject.deepObject.value) { 
    console.log('We found it!'); 
} 

seconda:

if(object.hasOwnProperty('innerObject') && object.innerObject.hasOwnProperty('deepObject') && object.innerObject.deepObject.hasOwnProperty('value')) { 
    console.log('We found it too!'); 
} 

Ma c'è un modo per fare un controllo profondo? Diciamo, qualcosa di simile:

object['innerObject.deepObject.value'] 

o

object.hasOwnProperty('innerObject.deepObject.value') 
+0

Certo, utilizzando qualsiasi di un numero, biblioteche che lo sostengono –

+0

È possibile scrivere facilmente una funzione che accetta una stringa del genere, la divide in una matrice di nomi di proprietà e passa a un ciclo controllando se ciascuna proprietà esiste. – Barmar

risposta

11

Non c'è un modo integrato per questo tipo di controllo, ma è possibile implementare facilmente. Creare una funzione, passare una stringa che rappresenta il percorso di proprietà, dividere il percorso da . e iterare questo percorso:

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

si potrebbe fare un semplice metodo ricorsivo per fare questo.

Il metodo sarebbe iterazione (ricorsivamente) su tutte le 'oggetto' proprietà dell'oggetto che si passa e restituire true non appena ne trova una che contiene la proprietà si passa. Se nessun oggetto contiene tale proprietà, restituisce 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

Ho paura che questa funzione cercherà la proprietà in qualsiasi oggetto interno, non specifico. – BadVolt

+0

Oh, immagino di aver frainteso te. Bene, [** Viktor Bahtev's answer **] (http://stackoverflow.com/a/33445095/3928341) è la strada da percorrere allora. – nem035

+2

@nem bella soluzione largamente utilizzabile comunque! – wintvelt

3

Alternativa funzione ricorsiva: Loops oltre tutto chiavi oggetto, per un tasto qualsiasi verifica se si tratta di un oggetto e, in tal caso, si chiama in modo ricorsivo. In caso contrario, restituisce l'array con vero, falso, falso per qualsiasi tasto con il nome propName. Il .reduce quindi esegue il rollup della matrice attraverso un'istruzione o.

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 
     } 
    })           // 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: la sua soluzione interrompe alla prima trovato: @ risposta di nem ha mostrato come potrebbe essere più performante 'valore'.

0

Il mio approccio utilizzava i blocchi try/catch. Perché non mi piace passare percorsi di proprietà profondi in stringhe. Sono un ragazzo pigro a cui piace il completamento automatico :)

Gli oggetti Javascript vengono valutati in runtime. Pertanto, se si restituisce l'istruzione oggetto in una funzione di callback, tale istruzione non verrà valutata finché non viene richiamata la funzione di callback.

Quindi questa funzione racchiude semplicemente la funzione di callback all'interno di un'istruzione catch. Se cattura l'eccezione restituisce 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 
 
}

Quando si tratta di prestazioni, è difficile dire quale approccio è migliore. Sui miei test se le proprietà dell'oggetto esistono e l'affermazione ha esito positivo, ho notato che l'uso di try/catch può essere 2x 3 volte più veloce della divisione delle stringhe sulle chiavi e del controllo della presenza di chiavi nell'oggetto.

Ma se la proprietà non esiste a un certo punto, l'approccio prototipo restituisce il risultato quasi 7 volte più veloce.

vedere il test da soli: https://jsfiddle.net/yatki/382qoy13/2/

È possibile anche controllare la libreria che ho scritto qui: https://github.com/yatki/try-to-validate

0

provare questa soluzione piacevole e facile:

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