Практически все
программы на JavaScript используют
объекты. Как мы говорили, даже функции являются объектами определенного рода. Поэтому,
прежде чем углубляться в изучение этого языка, нам нужно знать основы работы с
объектами.
Зачем вообще
нужны объекты в JavaScript? Предположим, вы делаете сайт по
продаже книг. Будет разумно каждый товар (книгу) представить в виде объекта с
набором следующих свойств:
-
название;
-
автор;
-
число
страниц;
-
цена.
Создадим объект.
Для этого используется такой синтаксис:
[переменная =] {
ключ1: значение1,
ключ2: значение2,
…
ключN: значениеN
};
В нашем случае
все можно определить так:
let book = {
title: "название",
author: "автор",
nPages: 0,
price: 0
};
Этот объект
можно воспринимать как некий ящик, в котором 4 секции с именами title, author, nPage, price, которые
называются ключами, и соответствующими значениями.
На этот ящик
имеется ссылка book, через которую мы к нему обращаемся. Таким образом,
данные в объектах сохраняются по принципу
ключ: значение
записанных через
запятую. Причем, ключ может быть только строкой, а вот значение – любым типом
данных. Пара ключ-значение называется свойством объекта, иногда – полями
объекта. К каждому свойству можно обратиться по его ключу. Для этого сначала
пишется имя переменной (book), ставится точка и далее следует
название ключа:
console.log(book.title);
console.log(book.price);
и так далее. Так
как значение ключа может быть любым типом данных, добавим к нашему объекту еще
одно такое свойство:
Работает эта
строчка так: если в объекте нет ключа isSelled, то оно
добавляется и ему присваивается значение false. Если же такой
ключ уже существует, то его значение переписывается:
Здесь мы
изменили значение ранее добавленного ключа с false на true. Теперь наш
объект выглядит так.
Для удаления
свойств из объекта используется оператор delete, например:
удалит свойство nPages из объекта book. В этом можно
убедиться. Если мы попытаемся вывести его в консоль
console.log(book.nPages);
то увидим
значение undefined, которое, как
правило, соответствует несуществующему свойству объекта (если, конечно,
значение undefined явно не присвоено
какому-либо свойству).
Если мы хотим
точно убедиться есть ли какое-либо свойство в объекте, можно использовать
оператор in:
console.log("nPages" in book); //вернет false
console.log("author" in book); //вернет true
Данный оператор
возвратит false, если такого
свойства нет и true, если есть. Однако, его очень редко используют на
практике, обычно для проверки применяют строгое сравнение со значением udefined:
if(book.nPages === undefined) console.log("nPages не существует");
Далее, из
объекта можно удалить все свойства и получить пустой объект:
Это эквивалентно
созданию вот такого объекта:
или же, его
можно создать так:
В обоих случаях
результат будет одинаковый. Но, на практике, обычно используют вариант с фигурными
скобками {...}. Такое объявление называют литералом объекта или литеральной
нотацией.
Имя свойства
объекта может быть какой-нибудь фразой. В этом случае ее записывают явно как
строку в кавычках:
let book = {
title: "название",
author: "автор",
nPages: 0,
"size book": {height: 100, width: 20}
};
Здесь значением
свойства является объект с двумя полями: height и width. Но, обратиться
к такому полю через точку будет уже невозможно:
book.size book = {height: 100, width: 50};
так как в
названии ключа имеется пробел и мы получим синтаксическую ошибку. При обращении
к свойству через точку требуется, чтобы ключ именовался по тем же правилам, что
и имена переменных. Поэтому для доступа к ключу с фразой нужно применять другой
синтаксис – через квадратные скобки:
book["size book"] = {height: 100, width: 50};
console.log(book["size book"]);
Тогда все
сработает и данное свойство будет изменено на новую строку. Удаление такого
свойства работает аналогично:
delete book["size book"];
Квадратные
скобки также позволяют обратиться к свойству через переменную, например, так:
let keyName = "size book";
console.log(book[keyName]);
Это очень
удобный подход, например, если нужно обратиться к произвольному полю объекта, в
зависимости от выбора пользователя:
let keyName = prompt("Что вы хотите узнать о книге?", "title");
console.log(book[keyName]);
Запись через
точку такого не позволяет. Используя квадратные скобки, можно даже создавать
новое свойство через переменную, следующим образом:
let newKey = "color";
let car = {
model: "toyota",
[newKey]: "black",
};
console.log(car[newKey]);
Или, даже так:
let car = {
model: "toyota",
[newKey+"Car"]: "black",
};
console.log(car["colorCar"]);
Здесь мы
буквально создаем новое имя ключа непосредственно при объявлении объекта.
Подведём небольшой
итог: в большинстве случаев, когда имена свойств известны и просты,
используется запись через точку. Если же нам нужно что-то более сложное, то мы
используем квадратные скобки.
Далее, давайте
посмотрим на такой пример создания объекта внутри функции:
let car = createCar("toyota", "black");
function createCar(model, color) {
return {
model: model,
color: color
};
}
Ссылка на
созданный объект присваивается глобальной переменной car и через нее мы
уже можем работать с этим объектом:
Но, кроме того,
обратите внимание, что в момент создания название ключа совпадает с названием
переменной, которая ему присваивается. Это довольно частая ситуация в JavaScript, поэтому для
инициализации таких объектов был придуман упрощенный синтаксис:
function createCar(model, color) {
return {
model, //то же самое, что и model:model
color //то же самое, что и color:color
};
}
Эта запись
полностью эквивалентна предыдущей. Но использовать ее удобнее.
Рассмотрим
теперь такую задачу. Предположим, у нас имеется объект:
let book = {
title: "Муму",
author: "Тургенев",
price: 100,
nPages: 282
};
И мы хотим
вывести все его свойства в консоль. Это можно сделать с помощью специального
оператора цикла for in, следующим
образом:
for(let key in book) {
console.log(key+": "+book[key]);
}
Мы здесь внутри
цикла объявили переменную key, которая на каждой итерации цикла будет
принимать значение очередного ключа. Далее через оператор in мы указываем
объект, из которого будут читаться ключи и затем, в теле цикла выводим сам ключ
и через двоеточие – его значение.
Вот так можно
перебирать свойства любого объекта в JavaScript.
Используя этот
цикл for, мы можем
провести небольшое исследование и посмотреть как выстраиваются поля в объектах.
Например:
let phoneCodes = {
"7": "Россия",
"49": "Германия",
"41": "Швейцария",
"1": "США"
};
for (let code in phoneCodes) {
console.log(code);
}
Смотрите, при
выводе свойства упорядочились по номерам с 1 до 49. Несколько неожиданно, да?
Дело в том, что применительно к объектам существует такое правило: все
целочисленные имена ключей упорядочиваются по возрастанию значений. Всех другие
ключи сохраняются в порядке их написания. Например, если поставить перед каждой
цифрой символ +:
let phoneCodes = {
"+7": "Россия",
"+49": "Германия",
"+41": "Швейцария",
"+1": "США"
};
Они перестают
быть целочисленными и потому остаются на своих местах. Вот это следует иметь в
виду.
Далее,
рассмотрим особенности копирования объектов. В отличие от примитивных типов
данных, которые буквально копируются по значению. Например, здесь мы получим
две совершенно независимые строки:
let str1 = "hello";
let str2 = str1;
Объекты
копируются по ссылке, то есть, имея ссылку на какой-либо объект, например:
let book = {
title: "название",
author: "автор",
nPages: 0,
price: 0
};
И выполняя
присваивание:
будет
скопирована ссылка на объект, а не сам объект.
То есть, и book и lib будут указывать
на один и тот же объект. И мы можем дальше работать с объектом через любую
переменную:
lib.title = "Муму";
console.log(book.title);
Здесь у вас
может возникнуть вопрос: а как же можно создать клон объекта или делать его
структурные копии? Об этом мы подробно поговорим на последующих занятиях, чтобы
не перегружать информацией этот урок.
Пока вернемся к
ссылкам на объект. Они приводят к некоторым особенностям взаимодействия с
объектами. Например, задавая константную переменную:
мы тем не менее
можем свободно менять свойства объекта:
Почему? Дело в
том, что мы объявляем константой саму ссылку, а не сам объект, то есть, объект
можно свободно менять, на ссылку это никак не влияет, но вот если мы захотим
изменить ссылку:
то получим
сообщение об ошибке.
Следующий
момент. Сравнивая между собой два объекта:
console.log(book == lib);
console.log(book === lib);
мы фактически
сравниваем значения ссылок. И эти равенства будут истинны только в том случае,
если указывают на один и тот же объект. А вот в этом случае:
let obj1 = {};
let obj2 = {};
console.log(obj1 == obj2);
console.log(obj1 === obj2);
Получим false, так как
значения ссылок не равны между собой – они указывают на разные объекты.
При других
сравнениях типа obj1 > obj2 или с примитивом obj == 5 объекты преобразуются к
примитивным типам данных, а потом сравниваются: либо как строки, либо как
числа, либо как булевы значения и так далее.