прелиминарии
JavaScript имеет только один тип данных, который может содержать несколько значений: Object . Массив представляет собой особую форму объекта.
(Обычный) Объекты имеют вид
{key: value, key: value, ...}
Массивы имеют форму
[value, value, ...]
И массивы, и объекты предоставляют key -> value
структуру. Ключи в массиве должны быть числовыми, тогда как любая строка может использоваться в качестве ключа в объектах. Пары ключ-значение также называются «свойствами» .
Свойства могут быть доступны либо с использованием точечной нотации
const value = obj.someProperty;
или обозначение в скобках , если имя свойства не будет допустимым именем идентификатора JavaScript [spec] , или имя является значением переменной:
// 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];
По этой причине доступ к элементам массива возможен только в скобках:
const value = arr[5]; // arr.5 would be a syntax error
// property name / index as variable
const x = 5;
const value = arr[x];
Подождите ... как насчет JSON?
JSON - это текстовое представление данных, как XML, YAML, CSV и другие. Чтобы работать с такими данными, их сначала нужно преобразовать в типы данных JavaScript, то есть массивы и объекты (и как работать с ними только что объяснено). Как разобрать JSON объясняется в вопросе Parse JSON в JavaScript? ,
Дальнейшее чтение материала
Доступ к массивам и объектам является фундаментальным знанием JavaScript, и поэтому желательно прочитать руководство по MDN JavaScript , особенно разделы.
Доступ к вложенным структурам данных
Вложенная структура данных - это массив или объект, который ссылается на другие массивы или объекты, т.е. его значения являются массивами или объектами. К таким структурам можно получить доступ, последовательно применяя точечные или скобочные обозначения.
Вот пример:
const data = {
code: 42,
items: [{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}]
};
Давайте предположим, что мы хотим получить доступ name
ко второму элементу.
Вот как мы можем сделать это шаг за шагом:
Как мы видим data
, это объект, следовательно, мы можем получить доступ к его свойствам, используя точечную запись. items
Свойство получено следующим образом :
data.items
Значение является массивом, для доступа ко второму элементу мы должны использовать скобочную запись:
data.items[1]
Это значение является объектом, и мы снова используем точечную запись для доступа к name
свойству. Итак, мы в итоге получаем:
const item_name = data.items[1].name;
В качестве альтернативы, мы могли бы использовать скобочную запись для любого из свойств, особенно если имя содержит символы, которые сделали бы его недопустимым для использования точечной нотации:
const item_name = data['items'][1]['name'];
Я пытаюсь получить доступ к собственности, но я получаю только undefined
обратно?
В большинстве случаев у undefined
объекта / массива просто нет свойства с таким именем.
const foo = {bar: {baz: 42}};
console.log(foo.baz); // undefined
Используйте console.log
или console.dir
и проверьте структуру объекта / массива. Свойство, к которому вы пытаетесь получить доступ, может быть фактически определено для вложенного объекта / массива.
console.log(foo.bar.baz); // 42
Что если имена свойств являются динамическими, и я не знаю их заранее?
Если имена свойств неизвестны или мы хотим получить доступ ко всем свойствам объекта / элементов массива, мы можем использовать цикл for...in
[MDN] для объектов и цикл for
[MDN] для массивов для перебора всех свойств / элементов.
Объекты
Чтобы перебрать все свойства data
, мы можем перебрать объект так:
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
}
В зависимости от того, откуда берется объект (и что вы хотите сделать), вам, возможно, придется проверять на каждой итерации, является ли свойство действительно свойством объекта или унаследованным свойством. Вы можете сделать это с Object#hasOwnProperty
[MDN] .
В качестве альтернативы for...in
with hasOwnProperty
вы можете использовать Object.keys
[MDN] для получения массива имен свойств :
Object.keys(data).forEach(function(prop) {
// `prop` is the property name
// `data[prop]` is the property value
});
Массивы
Чтобы перебрать все элементы data.items
массива , мы используем for
цикл:
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`.
}
Можно также использовать for...in
для итерации массивов, но есть причины, по которым этого следует избегать: почему «for (var item in list)» с массивами считается плохой практикой в JavaScript? ,
С увеличением поддержки браузером ECMAScript 5, метод массива forEach
[MDN] также становится интересной альтернативой:
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)
});
В средах, поддерживающих ES2015 (ES6), вы также можете использовать цикл [MDN] , который работает не только для массивов, но и для любых итераций :for...of
for (const item of data.items) {
// `item` is the array element, **not** the index
}
В каждой итерации, for...of
непосредственно дает нам следующий элемент итерируемого, нет «индекса» для доступа или использования.
Что если «глубина» структуры данных мне неизвестна?
В дополнение к неизвестным ключам также может быть неизвестна «глубина» структуры данных (то есть, сколько вложенных объектов). Как получить доступ к глубоко вложенным свойствам, обычно зависит от точной структуры данных.
Но если структура данных содержит повторяющиеся узоры, например , представление двоичного дерева, то решение , как правило , включает в рекурсивно [Wikipedia] доступ для каждого уровня структуры данных.
Вот пример, чтобы получить первый листовой узел двоичного дерева:
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);
Более общий способ получить доступ к вложенной структуре данных с неизвестными ключами и глубиной - это проверить тип значения и действовать соответственно.
Вот пример, который добавляет все примитивные значения внутри вложенной структуры данных в массив (при условии, что он не содержит никаких функций). Если мы сталкиваемся с объектом (или массивом), мы просто toArray
снова вызываем это значение (рекурсивный вызов).
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));
Помощники
Поскольку структура сложного объекта или массива не обязательно очевидна, мы можем проверять значение на каждом шаге, чтобы решить, как двигаться дальше. console.log
[MDN] и console.dir
[MDN] помогают нам в этом. Например (вывод консоли Chrome):
> console.log(data.items)
[ Object, Object ]
Здесь мы видим, что data.items
это массив с двумя элементами, которые оба являются объектами. В консоли Chrome объекты могут быть даже расширены и проверены немедленно.
> console.log(data.items[1])
Object
id: 2
name: "bar"
__proto__: Object
Это говорит нам о том, что data.items[1]
это объект, и после его расширения мы видим, что у него есть три свойства id
, name
и __proto__
. Последнее является внутренним свойством, используемым для цепочки прототипов объекта. Тем не менее, цепочка прототипов и наследование выходят за рамки этого ответа.