Как мы знаем из главы «Типы данных», в JavaScript существует восемь типов данных. Семь из них называются «примитивными», поскольку их значения содержат только одну вещь (будь то строка, число или что-то еще).
Напротив, объекты используются для хранения наборов различных данных и более сложных сущностей с ключами. В JavaScript объекты проникают почти во все аспекты языка. Поэтому мы должны сначала понять их, прежде чем углубляться в что-либо еще.
Объект можно создать с помощью фигурных скобок {…}
с дополнительным списком свойств . Свойство представляет собой пару «ключ: значение», где key
— это строка (также называемая «именем свойства»), а value
может быть любым.
Мы можем представить объект как шкаф с подписанными файлами. Каждый фрагмент данных хранится в своем файле по ключу. Легко найти файл по его имени или добавить/удалить файл.
Пустой объект («пустой шкаф») может быть создан с использованием одного из двух синтаксисов:
пусть пользователь = новый объект(); // синтаксис "конструктора объекта" пусть пользователь = {}; // синтаксис "объектного литерала"
Обычно используются фигурные скобки {...}
. Это объявление называется литералом объекта .
Мы можем сразу поместить некоторые свойства в {...}
как пары «ключ: значение»:
let user = { // объект name: "John", // по ключу "name" сохраняем значение "John" age: 30 // по ключу "age" сохраняем значение 30 };
Свойство имеет ключ (также известный как «имя» или «идентификатор») перед двоеточием ":"
и значение справа от него.
В объекте user
есть два свойства:
Первое свойство имеет имя "name"
и значение "John"
.
Второй имеет имя "age"
и значение 30
.
Полученный объект user
можно представить как шкаф с двумя подписанными файлами с пометками «имя» и «возраст».
Мы можем добавлять, удалять и читать файлы из него в любое время.
Значения свойств доступны с использованием точечной записи:
// получаем значения свойств объекта: оповещение(имя_пользователя); // Джон предупреждение(пользователь.возраст); // 30
Значение может быть любого типа. Добавим логическое значение:
user.isAdmin = правда;
Чтобы удалить свойство, мы можем использовать оператор delete
:
удалить user.age;
Мы также можем использовать имена свойств, состоящие из нескольких слов, но тогда их необходимо заключить в кавычки:
пусть пользователь = { имя: «Джон», возраст: 30, "любит птиц": true // имя свойства, состоящее из нескольких слов, должно быть заключено в кавычки };
Последнее свойство в списке может заканчиваться запятой:
пусть пользователь = { имя: «Джон», возраст: 30, }
Это называется «конечная» или «висячая» запятая. Облегчает добавление/удаление/перемещение свойств, поскольку все линии становятся одинаковыми.
Для свойств, состоящих из нескольких слов, доступ через точку не работает:
// это приведет к синтаксической ошибке user.likes birds = true
JavaScript этого не понимает. Он думает, что мы обращаемся к user.likes
, а затем выдает синтаксическую ошибку, когда встречает неожиданных birds
.
Точка требует, чтобы ключ был допустимым идентификатором переменной. Это означает: не содержит пробелов, не начинается с цифры и не содержит специальных символов (допускаются $
и _
).
Существует альтернативное обозначение в квадратных скобках, которое работает с любой строкой:
пусть пользователь = {}; // набор пользователь["любит птиц"] = true; // получать alert(user["любит птиц"]); // истинный // удалить удалить пользователя["любит птиц"];
Теперь все в порядке. Обратите внимание, что строка внутри скобок правильно заключена в кавычки (подойдут любые типы кавычек).
Квадратные скобки также позволяют получить имя свойства в результате любого выражения (в отличие от буквальной строки), например, из переменной следующим образом:
let key = "любит птиц"; // то же, что и user["любит птиц"] = true; пользователь [ключ] = правда;
Здесь переменный key
может рассчитываться во время выполнения или зависеть от ввода пользователя. А затем мы используем его для доступа к свойству. Это дает нам большую гибкость.
Например:
пусть пользователь = { имя: «Джон», возраст: 30 }; let key = Prompt("Что вы хотите узнать о пользователе?", "имя"); // доступ по переменной предупреждение(пользователь[ключ]); // Джон (если ввести "имя")
Точечную запись нельзя использовать аналогичным образом:
пусть пользователь = { имя: «Джон», возраст: 30 }; пусть ключ = "имя"; предупреждение(user.key) // не определено
Мы можем использовать квадратные скобки в литерале объекта при создании объекта. Это называется вычисляемыми свойствами .
Например:
let Fruit = Prompt("Какие фрукты купить?", "яблоко"); пусть сумка = { [fruit]: 5, // имя свойства взято из переменной Fruit }; оповещение(мешок.яблоко); // 5, если фруктов="яблоко"
Смысл вычисляемого свойства прост: [fruit]
означает, что имя свойства должно быть взято из fruit
.
Итак, если посетитель введет "apple"
, bag
станет {apple: 5}
.
По сути, это работает так же, как:
let Fruit = Prompt("Какие фрукты купить?", "яблоко"); пусть сумка = {}; // берем имя свойства из переменной Fruit мешок[фрукты] = 5;
…Но выглядит красивее.
Мы можем использовать более сложные выражения внутри квадратных скобок:
пусть фрукты = 'яблоко'; пусть сумка = { [фрукты + «Компьютеры»]: 5 // Bag.appleComputers = 5 };
Квадратные скобки гораздо более эффективны, чем точечная запись. Они допускают любые имена свойств и переменные. Но их также сложнее писать.
Поэтому в большинстве случаев, когда имена свойств известны и просты, используется точка. А если нам нужно что-то более сложное, то переходим на квадратные скобки.
В реальном коде мы часто используем существующие переменные в качестве значений имен свойств.
Например:
функция makeUser(имя, возраст) { возвращаться { имя: имя, возраст: возраст, // ...другие свойства }; } let user = makeUser("Джон", 30); оповещение(имя_пользователя); // Джон
В приведенном выше примере свойства имеют те же имена, что и переменные. Вариант использования свойства из переменной настолько распространен, что существует специальное сокращение значения свойства, чтобы сделать его короче.
Вместо name:name
мы можем просто написать name
, вот так:
функция makeUser(имя, возраст) { возвращаться { имя, // то же, что и имя: имя age, // то же, что и age: age // ... }; }
Мы можем использовать как обычные свойства, так и сокращения в одном и том же объекте:
пусть пользователь = { имя, // то же, что имя:имя возраст: 30 };
Как мы уже знаем, переменная не может иметь имя, равное одному из зарезервированных в языке слов, таких как «for», «let», «return» и т. д.
Но для свойства объекта такого ограничения нет:
// с этими свойствами все в порядке пусть объект = { для: 1, пусть: 2, возврат: 3 }; предупреждение(obj.for + obj.let + obj.return); // 6
Короче говоря, ограничений на имена свойств нет. Это могут быть любые строки или символы (специальный тип идентификаторов, о котором мы поговорим позже).
Другие типы автоматически преобразуются в строки.
Например, число 0
становится строкой "0"
при использовании в качестве ключа свойства:
пусть объект = { 0: «тест» // то же, что «0»: «тест» }; // оба оповещения обращаются к одному и тому же свойству (число 0 преобразуется в строку "0") предупреждение(объект["0"]); // тест предупреждение (объект [0]); // тест (то же свойство)
Есть небольшая ошибка со специальным свойством __proto__
. Мы не можем установить для него значение, не являющееся объектом:
пусть объект = {}; объект.__proto__ = 5; // присваиваем номер оповещение(obj.__proto__); // [object Object] — значение является объектом, сработало не так, как задумано
Как мы видим из кода, присвоение примитиву 5
игнорируется.
В последующих главах мы рассмотрим особую природу __proto__
и предложим способы исправить такое поведение.
Примечательной особенностью объектов в JavaScript по сравнению со многими другими языками является возможность доступа к любому свойству. Если свойство не существует, ошибки не будет!
Чтение несуществующего свойства просто возвращает undefined
. Итак, мы можем легко проверить, существует ли свойство:
пусть пользователь = {}; Предупреждение (user.noSuchProperty === не определено); // true означает «нет такого свойства»
Для этого также существует специальный оператор "in"
.
Синтаксис:
«ключ» в объекте
Например:
let user = { name: «Джон», возраст: 30 }; alert("возраст" в пользователе); // правда, user.age существует alert("блабла" у пользователя); // ложь, user.blabla не существует
Обратите внимание, что слева от in
должно быть имя свойства . Обычно это строка в кавычках.
Если мы опустим кавычки, это означает, что переменная должна содержать фактическое имя, которое нужно протестировать. Например:
пусть пользователь = {возраст: 30}; пусть ключ = «возраст»; предупреждение (ключ пользователя); // true, свойство age существует
Почему существует оператор in
? Разве этого недостаточно для сравнения с undefined
?
Ну, в большинстве случаев сравнение с undefined
работает нормально. Но есть особый случай, когда он не работает, а "in"
работает корректно.
Это когда свойство объекта существует, но хранит undefined
:
пусть объект = { тест: не определен }; предупреждение(obj.test); // оно не определено, значит, такого свойства нет? alert("тест" в объекте); // правда, свойство существует!
В приведенном выше коде свойство obj.test
технически существует. Итак, оператор in
работает правильно.
Подобные ситуации случаются очень редко, потому что undefined
не следует присваивать явно. Чаще всего мы используем null
для «неизвестных» или «пустых» значений. Таким образом, оператор in
— это экзотический гость в коде.
Для обхода всех ключей объекта существует специальная форма цикла: for..in
. Это совершенно отличается от конструкции for(;;)
которую мы изучали ранее.
Синтаксис:
for (ключ в объекте) { // выполняет тело для каждого ключа среди свойств объекта }
Например, выведем все свойства user
:
пусть пользователь = { имя: «Джон», возраст: 30, isAdmin: правда }; for (введите пользователя) { // ключи предупреждение (ключ); // имя, возраст, isAdmin // значения для ключей предупреждение(пользователь[ключ]); // Джон, 30 лет, правда }
Обратите внимание, что все конструкции for позволяют нам объявлять переменную цикла внутри цикла, как здесь let key
.
Кроме того, мы могли бы использовать здесь другое имя переменной вместо key
. Например, также широко используется "for (let prop in obj)"
.
Упорядочены ли объекты? Другими словами, если мы пройдемся по объекту, получим ли мы все свойства в том же порядке, в котором они были добавлены? Можем ли мы положиться на это?
Короткий ответ: «упорядочены особым образом»: целочисленные свойства сортируются, остальные отображаются в порядке создания. Подробности следуют далее.
В качестве примера рассмотрим объект с телефонными кодами:
пусть коды = { «49»: «Германия», «41»: «Швейцария», «44»: «Великобритания», // .., «1»: «США» }; for (пусть кодируется в кодах) { предупреждение (код); // 1, 41, 44, 49 }
Объект может использоваться для предложения пользователю списка опций. Если мы создаем сайт в основном для немецкой аудитории, то, вероятно, мы хотим, чтобы 49
был первым.
Но если мы запустим код, то увидим совершенно другую картину:
США (1) идет первым
затем Швейцария (41) и так далее.
Телефонные коды располагаются в порядке возрастания, поскольку они являются целыми числами. Итак, мы видим 1, 41, 44, 49
.
Целочисленные свойства? Что это такое?
Термин «целочисленное свойство» здесь означает строку, которую можно преобразовать в целое число и обратно без изменений.
Итак, "49"
— это целочисленное имя свойства, потому что, когда оно преобразуется в целое число и обратно, оно остается тем же самым. А вот "+49"
и "1.2"
— нет:
// Number(...) явно преобразуется в число // Math.trunc — встроенная функция, удаляющая десятичную часть alert( String(Math.trunc(Number("49"))) ); // "49", то же, целочисленное свойство alert( String(Math.trunc(Number("+49"))) ); // "49", а не "+49" ⇒ не целочисленное свойство alert( String(Math.trunc(Number("1.2"))) ); // "1", а не "1.2" ⇒ не целочисленное свойство
…С другой стороны, если ключи нецелые, то они перечислены в порядке создания, например:
пусть пользователь = { имя: «Джон», фамилия: «Смит» }; user.age = 25; // добавляем еще один // нецелочисленные свойства перечислены в порядке создания for (let prop in user) { оповещение (реквизит); // имя, фамилия, возраст }
Итак, чтобы решить проблему с телефонными кодами, мы можем «схитрить», сделав коды нецелыми. Достаточно добавить знак плюса "+"
перед каждым кодом.
Так:
пусть коды = { "+49": "Германия", «+41»: «Швейцария», «+44»: «Великобритания», // .., «+1»: «США» }; for (пусть кодируется в кодах) { предупреждение(+код); // 49, 41, 44, 1 }
Теперь он работает так, как задумано.
Объекты представляют собой ассоциативные массивы с несколькими особенностями.
Они хранят свойства (пары ключ-значение), где:
Ключи свойств должны быть строками или символами (обычно строками).
Значения могут быть любого типа.
Чтобы получить доступ к свойству, мы можем использовать:
Обозначение через точку: obj.property
.
Обозначение в квадратных скобках obj["property"]
. Квадратные скобки позволяют получить ключ из переменной, например obj[varWithKey]
.
Дополнительные операторы:
Чтобы удалить свойство: delete obj.prop
.
Чтобы проверить, существует ли свойство с данным ключом: "key" in obj
.
Чтобы перебрать объект: цикл for (let key in obj)
.
То, что мы изучили в этой главе, называется «простым объектом» или просто Object
.
В JavaScript есть много других типов объектов:
Array
для хранения упорядоченных коллекций данных,
Date
для хранения информации о дате и времени,
Error
для хранения информации об ошибке.
…И так далее.
У них есть свои особенности, которые мы изучим позже. Иногда говорят что-то вроде «Тип массива» или «Тип даты», но формально они не являются собственными типами, а принадлежат одному типу данных «объект». И расширяют его разными способами.
Объекты в JavaScript очень мощные. Здесь мы только что коснулись поверхности действительно огромной темы. Мы будем тесно работать с объектами и узнавать о них больше в дальнейших частях урока.
важность: 5
Напишите код, по одной строке на каждое действие:
Создайте пустой объект user
.
Добавьте name
свойства со значением John
.
Добавьте surname
свойства со значением Smith
.
Измените значение name
на Pete
.
Удалите name
свойства из объекта.
пусть пользователь = {}; user.name = "Джон"; user.surname = "Смит"; user.name = "Пит"; удалить имя пользователя;
важность: 5
Напишите функцию isEmpty(obj)
, которая возвращает true
если объект не имеет свойств, false
в противном случае.
Должно работать так:
пусть расписание = {}; Предупреждение (isEmpty (расписание)); // истинный расписание["8:30"] = "вставать"; Предупреждение (isEmpty (расписание)); // ЛОЖЬ
Откройте песочницу с тестами.
Просто переберите объект и немедленно return false
если есть хотя бы одно свойство.
функция isEmpty(obj) { for (введите ключ obj) { // если цикл начался, значит есть свойство вернуть ложь; } вернуть истину; }
Откройте решение с тестами в песочнице.
важность: 5
У нас есть объект, хранящий зарплаты нашей команды:
пусть зарплаты = { Джон: 100, Энн: 160, Пит: 130 }
Напишите код для суммирования всех зарплат и сохранения в переменной sum
. Должно быть 390
в приведенном выше примере.
Если salaries
пусты, то результат должен быть 0
.
пусть зарплаты = { Джон: 100, Энн: 160, Пит: 130 }; пусть сумма = 0; for (введите зарплату) { сумма += зарплата[ключ]; } предупреждение (сумма); // 390
важность: 3
Создайте функцию multiplyNumeric(obj)
, которая умножает все числовые значения свойств obj
на 2
.
Например:
// перед вызовом пусть меню = { ширина: 200, высота: 300, title: "Мое меню" }; MultipleNumeric (меню); // после вызова меню = { ширина: 400, высота: 600, title: "Мое меню" };
Обратите внимание, что multiplyNumeric
не должен ничего возвращать. Он должен изменить объект на месте.
PS Используйте typeof
, чтобы проверить номер здесь.
Откройте песочницу с тестами.
функция MultipleNumeric(obj) { for (введите ключ obj) { if (typeof obj[key] == 'число') { объект [ключ] * = 2; } } }
Откройте решение с тестами в песочнице.