Czynności wstępne
JavaScript ma tylko jeden typ danych, który może zawierać wiele wartości: Object . Array jest specjalną formą obiektu.
(Zwykły) Obiekty mają formę
{key: value, key: value, ...}
Tablice mają formę
[value, value, ...]
Zarówno tablice, jak i obiekty ujawniają key -> value
strukturę. Klucze w tablicy muszą być numeryczne, podczas gdy dowolny ciąg może być używany jako klucz w obiektach. Pary klucz-wartość są również nazywane „właściwościami” .
Dostęp do właściwości można uzyskać za pomocą notacji kropkowej
const value = obj.someProperty;
lub notacja w nawiasach , jeśli nazwa właściwości nie byłaby prawidłową nazwą identyfikatora JavaScript [spec] lub nazwa jest wartością zmiennej:
// the space is not a valid character in identifier names
const value = obj["some Property"];
// property name as variable
const name = "some Property";
const value = obj[name];
Z tego powodu dostęp do elementów tablicy można uzyskać tylko za pomocą notacji w nawiasach:
const value = arr[5]; // arr.5 would be a syntax error
// property name / index as variable
const x = 5;
const value = arr[x];
Poczekaj ... a co z JSON?
JSON to tekstowa reprezentacja danych, podobnie jak XML, YAML, CSV i inne. Aby pracować z takimi danymi, należy najpierw przekonwertować je na typy danych JavaScript, tj. Tablice i obiekty (a właśnie wyjaśniono, jak z nimi pracować). Jak parsować JSON wyjaśniono w pytaniu Parse JSON w JavaScript? .
Dalsze materiały do czytania
Jak uzyskać dostęp do tablic i obiektów jest podstawową wiedzą o JavaScript, dlatego wskazane jest przeczytanie MDN JavaScript Guide , zwłaszcza sekcji
Dostęp do zagnieżdżonych struktur danych
Zagnieżdżona struktura danych to tablica lub obiekt, który odnosi się do innych tablic lub obiektów, tzn. Jego wartościami są tablice lub obiekty. Dostęp do takich struktur można uzyskać, stosując kolejno notację kropkową lub nawiasową.
Oto przykład:
const data = {
code: 42,
items: [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}]
};
Załóżmy, że chcemy uzyskać dostęp name
do drugiego elementu.
Oto jak możemy to zrobić krok po kroku:
Jak widzimy, data
jest obiektem, dlatego możemy uzyskać dostęp do jego właściwości za pomocą notacji kropkowej. items
Nieruchomość jest dostępna w następujący sposób:
data.items
Wartością jest tablica, aby uzyskać dostęp do jej drugiego elementu, musimy użyć notacji nawiasowej:
data.items[1]
Ta wartość jest obiektem i ponownie używamy notacji kropkowej, aby uzyskać dostęp do name
właściwości. W końcu otrzymujemy:
const item_name = data.items[1].name;
Alternatywnie, moglibyśmy użyć notacji nawiasowej dla dowolnej właściwości, szczególnie jeśli nazwa zawierała znaki, które spowodowałyby, że byłaby niepoprawna dla użycia notacji kropkowej:
const item_name = data['items'][1]['name'];
Próbuję uzyskać dostęp do nieruchomości, ale dostaję tylko z undefined
powrotem?
W większości przypadków undefined
obiekt / tablica po prostu nie ma właściwości o tej nazwie.
const foo = {bar: {baz: 42}};
console.log(foo.baz); // undefined
Użyj console.log
lub console.dir
i sprawdź strukturę obiektu / tablicy. Właściwość, do której próbujesz uzyskać dostęp, może być faktycznie zdefiniowana w zagnieżdżonym obiekcie / tablicy.
console.log(foo.bar.baz); // 42
Co się stanie, jeśli nazwy właściwości są dynamiczne i nie znam ich wcześniej?
Jeśli nazwy właściwości są nieznane lub chcemy uzyskać dostęp do wszystkich właściwości obiektu / elementów tablicy, możemy użyć pętli for...in
[MDN] dla obiektów i pętli for
[MDN] dla tablic, aby iterować wszystkie właściwości / elementy.
Obiekty
Aby iterować po wszystkich właściwościach data
, możemy iterować po obiekcie w następujący sposób:
for (const prop in data) {
// `prop` contains the name of each property, i.e. `'code'` or `'items'`
// consequently, `data[prop]` refers to the value of each property, i.e.
// either `42` or the array
}
W zależności od tego, skąd pochodzi obiekt (i co chcesz zrobić), w każdej iteracji może być konieczne sprawdzenie, czy właściwość jest rzeczywiście własnością obiektu, czy jest dziedziczoną właściwością. Możesz to zrobić za pomocą Object#hasOwnProperty
[MDN] .
Alternatywnie do for...in
z hasOwnProperty
można użyć Object.keys
[MDN], aby uzyskać tablicę nazw właściwości :
Object.keys(data).forEach(function(prop) {
// `prop` is the property name
// `data[prop]` is the property value
});
Tablice
Aby wykonać iterację po wszystkich elementach data.items
tablicy , używamy for
pętli:
for(let i = 0, l = data.items.length; i < l; i++) {
// `i` will take on the values `0`, `1`, `2`,..., i.e. in each iteration
// we can access the next element in the array with `data.items[i]`, example:
//
// var obj = data.items[i];
//
// Since each element is an object (in our example),
// we can now access the objects properties with `obj.id` and `obj.name`.
// We could also use `data.items[i].id`.
}
Można również użyć for...in
do iteracji po tablicach, ale istnieją powody, dla których należy tego unikać: Dlaczego „for (var item in list)” z tablicami jest uważane za złą praktykę w JavaScript? .
Wraz z rosnącą obsługą przeglądarki ECMAScript 5 metoda tablicowa forEach
[MDN] staje się również interesującą alternatywą:
data.items.forEach(function(value, index, array) {
// The callback is executed for each element in the array.
// `value` is the element itself (equivalent to `array[index]`)
// `index` will be the index of the element in the array
// `array` is a reference to the array itself (i.e. `data.items` in this case)
});
W środowiskach obsługujących ES2015 (ES6) można również użyć pętli [MDN] , która działa nie tylko dla tablic, ale dla każdej iterowalnej :for...of
for (const item of data.items) {
// `item` is the array element, **not** the index
}
W każdej iteracji for...of
bezpośrednio daje nam kolejny element iteracji, nie ma „indeksu”, do którego można by uzyskać dostęp lub z którego można by korzystać.
Co jeśli „głębia” struktury danych jest mi nieznana?
Oprócz nieznanych kluczy „głębia” struktury danych (tj. Ile ma zagnieżdżonych obiektów), może być również nieznana. Sposób uzyskiwania dostępu do głęboko zagnieżdżonych właściwości zwykle zależy od dokładnej struktury danych.
Ale jeśli struktura danych zawiera powtarzające się wzorce, np. Reprezentację drzewa binarnego, rozwiązanie zazwyczaj obejmuje rekursywny dostęp [Wikipedia] do każdego poziomu struktury danych.
Oto przykład, aby uzyskać pierwszy węzeł liścia drzewa binarnego:
function getLeaf(node) {
if (node.leftChild) {
return getLeaf(node.leftChild); // <- recursive call
}
else if (node.rightChild) {
return getLeaf(node.rightChild); // <- recursive call
}
else { // node must be a leaf node
return node;
}
}
const first_leaf = getLeaf(root);
const root = {
leftChild: {
leftChild: {
leftChild: null,
rightChild: null,
data: 42
},
rightChild: {
leftChild: null,
rightChild: null,
data: 5
}
},
rightChild: {
leftChild: {
leftChild: null,
rightChild: null,
data: 6
},
rightChild: {
leftChild: null,
rightChild: null,
data: 7
}
}
};
function getLeaf(node) {
if (node.leftChild) {
return getLeaf(node.leftChild);
} else if (node.rightChild) {
return getLeaf(node.rightChild);
} else { // node must be a leaf node
return node;
}
}
console.log(getLeaf(root).data);
Bardziej ogólnym sposobem uzyskania dostępu do zagnieżdżonej struktury danych z nieznanymi kluczami i głębokością jest przetestowanie typu wartości i odpowiednie działanie.
Oto przykład, który dodaje wszystkie pierwotne wartości w zagnieżdżonej strukturze danych do tablicy (zakładając, że nie zawiera żadnych funkcji). Jeśli napotkamy obiekt (lub tablicę), po prostu wywołujemy toArray
ponownie tę wartość (wywołanie rekurencyjne).
function toArray(obj) {
const result = [];
for (const prop in obj) {
const value = obj[prop];
if (typeof value === 'object') {
result.push(toArray(value)); // <- recursive call
}
else {
result.push(value);
}
}
return result;
}
const data = {
code: 42,
items: [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}]
};
function toArray(obj) {
const result = [];
for (const prop in obj) {
const value = obj[prop];
if (typeof value === 'object') {
result.push(toArray(value));
} else {
result.push(value);
}
}
return result;
}
console.log(toArray(data));
Pomocnicy
Ponieważ struktura złożonego obiektu lub tablicy niekoniecznie jest oczywista, możemy sprawdzić wartość na każdym kroku, aby zdecydować, jak przejść dalej. console.log
[MDN] i console.dir
[MDN] pomagają nam to zrobić. Na przykład (dane wyjściowe konsoli Chrome):
> console.log(data.items)
[ Object, Object ]
Widzimy tutaj, że data.items
jest to tablica z dwoma elementami, które są obiektami. W konsoli Chrome obiekty mogą być nawet powiększane i sprawdzane natychmiast.
> console.log(data.items[1])
Object
id: 2
name: "bar"
__proto__: Object
To mówi nam, że data.items[1]
jest to obiekt, a po rozwinięciu widzimy, że ma on trzy właściwości id
, name
oraz __proto__
. Ta ostatnia jest wewnętrzną właściwością stosowaną w łańcuchu prototypów obiektu. Jednak łańcuch prototypów i dziedziczenie nie mieszczą się w tej odpowiedzi.