Jeśli mam odwołanie do obiektu:
var test = {};
które potencjalnie (ale nie natychmiast) zagnieżdżą obiekty, takie jak:
{level1: {level2: {level3: "level3"}}};
Jaki jest najlepszy sposób sprawdzenia istnienia własności w głęboko zagnieżdżonych obiektach?
alert(test.level1);
daje undefined
, ale alert(test.level1.level2.level3);
zawodzi.
Obecnie robię coś takiego:
if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
alert(test.level1.level2.level3);
}
ale zastanawiałem się, czy jest lepszy sposób.
javascript
object
properties
nested
użytkownik113716
źródło
źródło
Odpowiedzi:
Musisz to zrobić krok po kroku, jeśli nie chcesz,
TypeError
ponieważ jeśli jeden z członków jestnull
lubundefined
i próbujesz uzyskać dostęp do członka, zostanie zgłoszony wyjątek.Możesz albo po prostu
catch
wyjątek, albo utworzyć funkcję testującą istnienie wielu poziomów, coś w tym rodzaju:AKTUALIZACJA ES6:
Oto krótsza wersja oryginalnej funkcji, wykorzystująca funkcje i rekurencję ES6 (ma również odpowiednią formę wywołania ogona ):
Jeśli jednak chcesz uzyskać wartość zagnieżdżonej właściwości i nie tylko sprawdzić jej istnienie, oto prosta funkcja jednowierszowa:
Powyższa funkcja pozwala uzyskać wartość zagnieżdżonych właściwości, w przeciwnym razie zwróci
undefined
.AKTUALIZACJA 17.10.2019:
Opcjonalnie propozycja łańcuchowym osiągnęła etap 3 na proces komisji ECMAScript , pozwoli to na bezpieczne dostępu właściwości głęboko zagnieżdżonych, za pomocą tokena
?.
, nowa opcja operator łańcuchowym :Jeśli którykolwiek z dostępnych poziomów jest
null
lubundefined
wyrażenie zostanie rozwiązaneundefined
samodzielnie.Ta propozycja pozwala również bezpiecznie obsługiwać wywołania metod:
Powyższe wyrażenie wygeneruje
undefined
ifobj
,obj.level1
lubobj.level1.method
arenull
orundefined
, w przeciwnym razie wywoła funkcję.Możesz zacząć grać z tą funkcją w Babel za pomocą opcjonalnej wtyczki łańcuchowej .Od wersji Babel 7.8.0 ES2020 jest domyślnie obsługiwany
Sprawdź ten przykład na Babel REPL.
🎉🎉 AKTUALIZACJA: grudzień 2019 🎉🎉
Opcjonalna propozycja łączenia ostatecznie osiągnęła etap 4 na posiedzeniu komitetu TC39 w grudniu 2019 r. Oznacza to, że ta funkcja będzie częścią standardu ECMAScript 2020 .
źródło
arguments
tak naprawdę nie jest tablicą.Array.prototype.slice.call(arguments)
konwertuje go na formalną tablicę. Dowiedz sięvar obj = arguments[0];
zacząć i zacząć odvar i = 1
zamiast kopiowaćarguments
obiektargs
deklaracja zmiennej może zostać usunięta i...args
może być wykorzystana jako drugi argumentcheckNested
metody. developer.mozilla.org/en/docs/Web/JavaScript/Reference/…Oto wzór, który wybrałem od Olivera Steele :
W rzeczywistości cały artykuł jest dyskusją na temat tego, jak to zrobić w javascript. Postanawia używać powyższej składni (co nie jest trudne do odczytania, gdy się przyzwyczaisz) jako idiomu.
źródło
(test || {})
to, że jeśli test jest niezdefiniowany, to robisz({}.level1 || {})
. Oczywiście{}.level1
jest niezdefiniowany, więc oznacza to, że robisz{}.level2
i tak dalej.test
nie zostanie zadeklarowany, wystąpi błąd ReferenceError , ale to nie jest problem, ponieważ jeśli nie zostanie zadeklarowany, będzie błąd do naprawienia, więc błąd jest dobrą rzeczą.if(test.level1 && test.level1.level2 && test.level1.level2.level3)
Aktualizacja
Wygląda na to, że lodash został dodany
_.get
do wszystkich potrzeb związanych z zagnieżdżaniem nieruchomości.https://lodash.com/docs#get
Poprzednia odpowiedź
Użytkownicy lodash mogą korzystać z lodash.contrib, który ma kilka metod łagodzących ten problem .
getPath
Podpis:
_.getPath(obj:Object, ks:String|Array)
Pobiera wartość na dowolnej głębokości w zagnieżdżonym obiekcie na podstawie ścieżki opisanej przez podane klucze. Klucze mogą być podawane jako tablica lub jako ciąg rozdzielany kropkami. Zwraca,
undefined
jeśli ścieżka nie może zostać osiągnięta.źródło
function isPathDefined(object, path) { return typeof _.getPath(object, path) !== 'undefined'; }
_.get(countries, 'greece.sparta.playwright', 'default'); // → 'default' _.has(countries, 'greece.spart.playwright') // → false
var url = _.get(e, 'currentTarget.myurl', null) || _.get(e, 'currentTarget.attributes.myurl.nodeValue', null) || null
Przeprowadziłem testy wydajności (dziękuję cdMinix za dodanie lodash) dla niektórych sugestii zaproponowanych do tego pytania z wynikami wymienionymi poniżej.
Object Wrap (autor: Oliver Steele) - 34% - najszybciej
Oryginalne rozwiązanie (sugerowane w pytaniu) - 45%
checkNested - 50%
get_if_exist - 52%
validChain - 54%
objHasKeys - 63%
nestedPropertyExists - 69%
_.get - 72%
najgłębsze - 86%
smutni klauni - 100% - najwolniej
źródło
_.get()
? jak skuteczny jest w porównaniu do tych odpowiedzi?reduce
lubtry/catch
nie dla wygody.try { test.level1.level2.level3 } catch (e) { // some logger e }
Można odczytać właściwość obiektu na każdej głębokości, jeśli uchwyt nazwę jak struna:
't.level1.level2.level3'
.Zwraca,
undefined
jeśli którykolwiek z segmentów jestundefined
.źródło
Jeśli kodujesz w środowisku ES6 (lub używasz 6to5 ), możesz skorzystać ze składni funkcji strzałek :
Jeśli chodzi o wydajność, nie ma ograniczenia wydajności za korzystanie
try..catch
bloku, jeśli właściwość jest ustawiona. Jeśli właściwość jest rozbrojona, ma to wpływ na wydajność.Rozważ użycie
_.has
:źródło
try-catch
podejście jest najlepszą odpowiedzią. Istnieje filozoficzna różnica między zapytaniem o obiekt dla jego typu, a założeniem, że interfejs API istnieje, i odpowiednio się nie powiedzie, jeśli nie będzie. To drugie jest bardziej odpowiednie w luźno pisanych językach. Zobacz stackoverflow.com/a/408305/2419669 .try-catch
Podejście jest również znacznie jaśniejsze niżif (foo && foo.bar && foo.bar.baz && foo.bar.baz.qux) { ... }
.Co powiesz na
źródło
hasOwnProperty()
n razy?Odpowiedź ES6, dokładnie przetestowana :)
→ patrz Codepen z pełnym pokryciem testowym
źródło
===
różne falsys i dodałem test. Jeśli masz lepszy pomysł, daj mi znać).false
. A potem możesz chcieć mieć wartość w swoim obiekcie ustawioną naundefined
(wiem, że to dziwne, ale JS). Zrobiłem dodatnią fałszywą wartość ustawioną na'Prop not Found'
:const hasTruthyProp = prop => prop === 'Prop not found' ? false : true const path = obj => path => path.reduce((obj, prop) => { return obj && obj.hasOwnProperty(prop) ? obj[prop] : 'Prop not found' }, obj) const myFunc = compose(hasTruthyProp, path(obj))
Możesz również użyć opcjonalnej kombinacji łańcuchowej tc39 wraz z babel 7 - tc39-propozycja-opcjonalna łączenie łańcuchów
Kod wyglądałby tak:
źródło
Wypróbowałem podejście rekurencyjne:
W
! keys.length ||
wyrzuca z rekursji więc nie uruchomić funkcję bez pozostawionych do testu kluczy. Testy:Używam go do drukowania przyjaznego widoku html wiązki obiektów o nieznanych kluczach / wartościach, np .:
źródło
Myślę, że poniższy skrypt daje bardziej czytelną reprezentację.
zadeklaruj funkcję:
użyj tego w następujący sposób:
Nazywam to „techniką smutnego klauna”, ponieważ używa znaku o (
EDYTOWAĆ:
tutaj jest wersja dla TypeScript
daje kontrolę typu w czasie kompilacji (a także inteligencję, jeśli używasz narzędzia takiego jak Visual Studio)
użycie jest takie samo:
ale tym razem intellisense działa!
ponadto możesz ustawić wartość domyślną:
źródło
°0o <°(())))><
Object
typu. +1.stworzyć globalny
function
i wykorzystaj w całym projekcieSpróbuj tego
jeśli warunek
źródło
Jednym prostym sposobem jest:
try/catch
Połowy przypadków, w przypadku każdego z wyższego poziomu obiektów, takich jak badania, test.level1, test.level1.level2 nie są określone.źródło
Nie widziałem żadnego przykładu osoby używającej proxy
Więc wymyśliłem własne. Wspaniałą rzeczą jest to, że nie trzeba interpolować ciągów. Możesz faktycznie zwrócić funkcję
obiektuzdolną do tworzenia łańcuchów i robić z nią magiczne rzeczy. Możesz nawet wywoływać funkcje i uzyskiwać indeksy tablic w celu sprawdzenia głębokich obiektówPokaż fragment kodu
Powyższy kod działa dobrze dla rzeczy synchronicznych. Ale jak przetestowałbyś coś tak asynchronicznego, jak to wywołanie ajax? Jak to testujesz? co jeśli odpowiedź nie jest json, gdy zwraca błąd 500 HTTP?
upewnij się, że możesz użyć asynchronizacji / oczekiwania na pozbycie się niektórych wywołań zwrotnych. A gdybyś mógł to zrobić jeszcze bardziej magicznie? coś, co wygląda tak:
Prawdopodobnie zastanawiasz się, gdzie są wszystkie obietnice i
.then
łańcuchy ... to może blokować wszystko, co wiesz ... ale używając tej samej techniki proxy z obietnicą możesz faktycznie przetestować głęboko zagnieżdżoną złożoną ścieżkę pod kątem jej istnienia bez pisania żadnej funkcjiPokaż fragment kodu
źródło
Na podstawie tej odpowiedzi wymyśliłem tę ogólną funkcję,
ES2015
która rozwiązałaby problemźródło
function validChain (object, path) { return path.split('.').reduce((a, b) => (a || { })[b], object) !== undefined }
Stworzyłem małą funkcję, aby bezpiecznie uzyskać właściwości zagnieżdżonego obiektu.
Lub prostsza, ale nieco nieczytelna wersja:
Lub nawet krócej, ale bez rezerwy na fladze falsy:
Mam test z:
A oto kilka testów:
Aby zobaczyć cały kod z dokumentacją i próbami, które wypróbowałem, możesz sprawdzić moją listę github: https://gist.github.com/vsambor/3df9ad75ff3de489bbcb7b8c60beebf4#file-javascriptgetnestedvalues-js
źródło
Krótsza wersja doskonałej odpowiedzi @ CMS na ES5:
Z podobnym testem:
źródło
checkObjHasKeys(test, ['level1', 'level2', 'asdf', 'asdf']);
success = false;
nareturn false
. Powinieneś wyskoczyć, gdy wiesz, że się psuje, nic głębszego nie może istnieć, gdy jest zerowe lub niezdefiniowane. Pozwoliłoby to uniknąć błędów w głęboko zagnieżdżonych elementach, ponieważ oczywiście one również nie istnieją.Szukałem wartości, która ma zostać zwrócona, jeśli właściwość istnieje, więc zmodyfikowałem odpowiedź przez CMS powyżej. Oto, co wymyśliłem:
źródło
Odpowiedź podana przez CMS działa dobrze z następującą modyfikacją również dla kontroli zerowych
źródło
Na podstawie tej odpowiedzi opracowano następujące opcje . To samo drzewo dla obu:
Zatrzymaj wyszukiwanie, gdy nie jest zdefiniowane
Zapewniaj każdy poziom jeden po drugim
źródło
Wiem, że to pytanie jest stare, ale chciałem zaoferować rozszerzenie, dodając je do wszystkich obiektów. Wiem, że ludzie nie lubią używać prototypu Object do rozszerzonej funkcjonalności obiektu, ale nie znajduję nic łatwiejszego niż robienie tego. Ponadto jest to teraz dozwolone w przypadku metody Object.defineProperty .
Teraz, aby przetestować dowolną właściwość w dowolnym obiekcie, możesz po prostu zrobić:
Oto jsfiddle do przetestowania go, aw szczególności zawiera on jQuery, który psuje się, jeśli zmodyfikujesz Object.prototype bezpośrednio z powodu wyliczenia właściwości. Powinno to działać poprawnie z bibliotekami stron trzecich.
źródło
Myślę, że to niewielka poprawa (zmienia się w 1-liniowy):
Działa to, ponieważ operator && zwraca końcowy operand, który ocenił (i powoduje zwarcie).
źródło
Działa to ze wszystkimi obiektami i tablicami :)
dawny:
to jest moja ulepszona wersja odpowiedzi Briana
Użyłem _has jako nazwy właściwości, ponieważ może powodować konflikt z istniejącą ma właściwość (np. Mapy)
Oto skrzypce
źródło
Oto moje zdanie na ten temat - większość z tych rozwiązań ignoruje przypadek zagnieżdżonej tablicy jak w:
Mogę chcieć sprawdzić
obj.l2[0].k
Dzięki funkcji poniżej możesz to zrobić
deeptest('l2[0].k',obj)
Funkcja zwróci true, jeśli obiekt istnieje, w przeciwnym razie false
źródło
Teraz możemy także używać
reduce
do przechodzenia przez zagnieżdżone klucze:źródło
Możesz to zrobić za pomocą funkcji rekurencyjnej. Działa to nawet wtedy, gdy nie znasz wszystkich nazw zagnieżdżonych kluczy Object.
źródło
jest tu funkcja na codecode (safeRead), która zrobi to w bezpieczny sposób ... tj
jeśli jakakolwiek właściwość jest pusta lub niezdefiniowana, zwracany jest pusty ciąg
źródło
W oparciu o poprzedni komentarz , oto kolejna wersja, w której główny obiekt nie mógł zostać zdefiniowany:
źródło
Napisałem własną funkcję, która podąża pożądaną ścieżkę i ma dobrą i złą funkcję wywołania zwrotnego.
Stosowanie:
źródło
źródło