880 Stimmen

Test auf Vorhandensein eines verschachtelten JavaScript-Objektschlüssels

Wenn ich einen Verweis auf ein Objekt habe:

var test = {};

die potenziell (aber nicht sofort) verschachtelte Objekte haben wird, etwa so:

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

Wie lässt sich das Vorhandensein von Eigenschaften in tief verschachtelten Objekten am besten überprüfen?

alert(test.level1); ergibt undefined aber alert(test.level1.level2.level3); scheitert.

Ich mache derzeit etwas Ähnliches:

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

aber ich habe mich gefragt, ob es einen besseren Weg gibt.

1 Stimmen

Sie sollten sich vielleicht eine tangential verwandte Frage ansehen, die kürzlich gestellt wurde stackoverflow.com/questions/2525943/

0 Stimmen

0 Stimmen

Ein paar Vorschläge dazu: stackoverflow.com/a/18381564/1636522

734voto

Christian C. Salvadó Punkte 763569

Sie müssen Schritt für Schritt vorgehen, wenn Sie nicht wollen, dass ein TypeError denn wenn eines der Mitglieder null o undefined und Sie versuchen, auf ein Mitglied zuzugreifen, wird eine Ausnahme ausgelöst.

Sie können entweder einfach catch die Ausnahme, oder erstellen Sie eine Funktion zum Testen des Vorhandenseins mehrerer Ebenen, etwa so:

function checkNested(obj /*, level1, level2, ... levelN*/) {
  var args = Array.prototype.slice.call(arguments, 1);

  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

ES6 UPDATE:

Hier ist eine kürzere Version der ursprünglichen Funktion, die ES6-Funktionen und Rekursion verwendet (sie ist auch in richtiger Schwanzruf Form):

function checkNested(obj, level,  ...rest) {
  if (obj === undefined) return false
  if (rest.length == 0 && obj.hasOwnProperty(level)) return true
  return checkNested(obj[level], ...rest)
}

Wenn Sie jedoch den Wert einer verschachtelten Eigenschaft abrufen und nicht nur ihr Vorhandensein überprüfen möchten, finden Sie hier eine einfache einzeilige Funktion:

function getNested(obj, ...args) {
  return args.reduce((obj, level) => obj && obj[level], obj)
}

const test = { level1:{ level2:{ level3:'level3'} } };
console.log(getNested(test, 'level1', 'level2', 'level3')); // 'level3'
console.log(getNested(test, 'level1', 'level2', 'level3', 'length')); // 6
console.log(getNested(test, 'level1', 'level2', 'foo')); // undefined
console.log(getNested(test, 'a', 'b')); // undefined

Die obige Funktion ermöglicht es Ihnen, den Wert von verschachtelten Eigenschaften zu erhalten, andernfalls wird zurückgegeben undefined .

UPDATE 2019-10-17:

El optionaler Verkettungsvorschlag erreichte Stufe 3 der Verfahren des ECMAScript-Ausschusses können Sie sicher auf tief verschachtelte Eigenschaften zugreifen, indem Sie das Token ?. die neue optionaler Verkettungsoperator :

const value = obj?.level1?.level2?.level3 

Wenn eine der aufgerufenen Ebenen null o undefined wird der Ausdruck aufgelöst in undefined von selbst.

Der Vorschlag ermöglicht es Ihnen auch, Methodenaufrufe sicher zu handhaben:

obj?.level1?.method();

Der obige Ausdruck ergibt undefined si obj , obj.level1 , oder obj.level1.method sind null o undefined , ansonsten wird die Funktion aufgerufen.

Sie können mit dieser Funktion mit Babel spielen, indem Sie die optionales Verkettungs-Plugin .

Desde Babel 7.8.0 ES2020 wird standardmäßig unterstützt.

Siehe dieses Beispiel auf der Babel REPL.

UPDATE: Dezember 2019

Der fakultative Verkettungsvorschlag schließlich Stufe 4 erreicht in der Dezember-Sitzung 2019 des Ausschusses TC39. Das bedeutet, dass diese Funktion Teil der ECMAScript 2020 Standard.

0 Stimmen

Erstaunliche Logik Oo, was macht Array.prototype.slice.call(arguments)?

4 Stimmen

arguments ist nicht wirklich ein Array. Array.prototype.slice.call(arguments) wandelt es in ein formales Array um. Lernen Sie

24 Stimmen

Das wäre viel effizienter zu tun var obj = arguments[0]; und starten von var i = 1 anstatt das Kopieren der arguments Objekt

381voto

Gabe Moothart Punkte 30087

Hier ist ein Muster, das ich aufgeschnappt von Oliver Steele :

var level3 = (((test || {}).level1 || {}).level2 || {}).level3;
alert( level3 );

In der Tat, dass ganze Artikel ist eine Diskussion darüber, wie Sie dies tun können, in Javascript. Er begnügt sich damit, die obige Syntax (die nicht so schwer zu lesen ist, sobald man sich daran gewöhnt hat) als Idiom zu verwenden.

1 Stimmen

Wenn Sie also auf der ersten Ebene scheitern, müssen Sie das gesamte Rohr durchsuchen, um schließlich die undefined was vorhersehbar war. Nicht so interessant ;)

8 Stimmen

@wared Ich denke, es ist vor allem interessant, wie prägnant es ist. Es gibt eine detaillierte Diskussion der Leistungsmerkmale in dem verlinkten Beitrag. Ja, es führt immer alle Tests durch, aber es vermeidet die Erstellung von temporären Variablen, und Sie können {} zu einer Variablen aliasieren, wenn Sie den Overhead der Erstellung eines neuen leeren Objekts jedes Mal vermeiden wollen. In 99% der Fälle würde ich nicht erwarten, dass die Geschwindigkeit eine Rolle spielt, und in den Fällen, in denen dies der Fall ist, gibt es keinen Ersatz für Profiling.

0 Stimmen

Ich verstehe jetzt, ich war offenbar off topic :) Das heißt, ich mag die Idee eines wiederverwendbaren leeren Objekts.

291voto

Austin Pray Punkte 4846

Update

Sieht aus wie Lodash hat hinzugefügt _.get für alle Ihre Bedürfnisse in Bezug auf verschachtelte Eigenschaften.

_.get(countries, 'greece.sparta.playwright')

https://lodash.com/docs#get


Vorherige Antwort

lodash Benutzer können genießen lodash.contrib die eine einige Methoden, die dieses Problem entschärfen .

getPath

Unterschrift: _.getPath(obj:Object, ks:String|Array)

Ruft den Wert in einer beliebigen Tiefe in einem verschachtelten Objekt ab, basierend auf dem Pfad, der durch die angegebenen Schlüssel. Die Schlüssel können als Array oder als punktgetrennte Zeichenkette angegeben werden. Rückgabe undefined wenn der Pfad nicht erreicht werden kann.

var countries = {
        greece: {
            athens: {
                playwright:  "Sophocles"
            }
        }
    }
};

_.getPath(countries, "greece.athens.playwright");
// => "Sophocles"

_.getPath(countries, "greece.sparta.playwright");
// => undefined

_.getPath(countries, ["greece", "athens", "playwright"]);
// => "Sophocles"

_.getPath(countries, ["greece", "sparta", "playwright"]);
// => undefined

0 Stimmen

Lodash braucht unbedingt eine Methode _.isPathDefined(obj, pathString).

0 Stimmen

@MatthewPayne Es wäre vielleicht nett, aber es ist wirklich nicht notwendig. Sie können es ganz einfach selbst machen function isPathDefined(object, path) { return typeof _.getPath(object, path) !== 'undefined'; }

13 Stimmen

Lodash selbst verfügt über die gleiche Funktionalität: _.get(countries, 'greece.sparta.playwright', 'default'); // 'default' _.has(countries, 'greece.spart.playwright') // false

238voto

unitario Punkte 5555

Ich habe getan Leistungsprüfungen (Dankeschön cdMinix für die Hinzufügung von Lodash) zu einigen der Vorschläge, die zu dieser Frage gemacht wurden, mit den unten aufgeführten Ergebnissen.

Haftungsausschluss Nr. 1 Die Umwandlung von Zeichenketten in Referenzen ist eine unnötige Meta-Programmierung, die wahrscheinlich am besten vermieden wird. Verlieren Sie nicht den Überblick über Ihre Referenzen, um damit zu beginnen. Lesen Sie mehr in dieser Antwort auf eine ähnliche Frage .

Haftungsausschluss #2 Wir sprechen hier von Millionen von Operationen pro Millisekunde. Es ist sehr unwahrscheinlich, dass einer dieser Vorgänge in den meisten Anwendungsfällen einen großen Unterschied machen würde. Entscheiden Sie sich für das, was am meisten Sinn macht, wenn Sie die Grenzen der einzelnen Systeme kennen. Ich würde mich für etwas entscheiden wie reduce aus Bequemlichkeit.

Objekt-Verpackung (von Oliver Steele) - 34 % - am schnellsten

var r1 = (((test || {}).level1 || {}).level2 || {}).level3;
var r2 = (((test || {}).level1 || {}).level2 || {}).foo;

Ursprüngliche Lösung (in der Frage vorgeschlagen) - 45%

var r1 = test.level1 && test.level1.level2 && test.level1.level2.level3;
var r2 = test.level1 && test.level1.level2 && test.level1.level2.foo;

checkNested - 50%

function checkNested(obj) {
  for (var i = 1; i < arguments.length; i++) {
    if (!obj.hasOwnProperty(arguments[i])) {
      return false;
    }
    obj = obj[arguments[i]];
  }
  return true;
}

get_if_exist - 52%

function get_if_exist(str) {
    try { return eval(str) }
    catch(e) { return undefined }
}

validChain - 54%

function validChain( object, ...keys ) {
    return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}

objHasKeys - 63%

function objHasKeys(obj, keys) {
  var next = keys.shift();
  return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}

nestedPropertyExists - 69%

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

_.get - 72%

Deeptest - 86%

function deeptest(target, s){
    s= s.split('.')
    var obj= target[s.shift()];
    while(obj && s.length) obj= obj[s.shift()];
    return obj;
}

traurige Clowns - 100% - am langsamsten

var o = function(obj) { return obj || {} };

var r1 = o(o(o(o(test).level1).level2).level3);
var r2 = o(o(o(o(test).level1).level2).foo);

19 Stimmen

Es ist zu beachten, dass ein Test umso LANGSAMER ist, je mehr % er hat

2 Stimmen

Was ist mit Lodash? _.get() ? wie leistungsfähig ist sie im Vergleich zu diesen Antworten?

1 Stimmen

Jede dieser Methoden ist je nach Situation langsamer oder schneller als andere. Wenn alle Schlüssel gefunden werden, könnte "Object Wrap" am schnellsten sein, aber wenn einer der Schlüssel nicht gefunden wird, könnte "Native solution/Original solution" schneller sein.

46voto

kennebec Punkte 98551

Sie können eine Objekteigenschaft in beliebiger Tiefe lesen, wenn Sie den Namen wie eine Zeichenkette behandeln: 't.level1.level2.level3' .

window.t={level1:{level2:{level3: 'level3'}}};

function deeptest(s){
    s= s.split('.')
    var obj= window[s.shift()];
    while(obj && s.length) obj= obj[s.shift()];
    return obj;
}

alert(deeptest('t.level1.level2.level3') || 'Undefined');

Sie gibt zurück undefined wenn eines der Segmente undefined .

3 Stimmen

Es ist erwähnenswert, dass diese Methode sehr performant ist, zumindest in Chrome, und in einigen Fällen die von @Claudiu modifizierte Version der ausgewählten Antwort übertrifft. Siehe Leistungstest hier: jsperf.com/check-if-deep-property-exists-with-willnotthrow

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X