Создание объектов и цикл for in

Практически все программы на 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);

и так далее. Так как значение ключа может быть любым типом данных, добавим к нашему объекту еще одно такое свойство:

book.isSelled = false;

Работает эта строчка так: если в объекте нет ключа isSelled, то оно добавляется и ему присваивается значение false. Если же такой ключ уже существует, то его значение переписывается:

book.isSelled = true;

Здесь мы изменили значение ранее добавленного ключа с false на true. Теперь наш объект выглядит так.

Для удаления свойств из объекта используется оператор delete, например:

delete book.nPages;

удалит свойство 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 = {};

или же, его можно создать так:

let book = new Object();

В обоих случаях результат будет одинаковый. Но, на практике, обычно используют вариант с фигурными скобками {...}. Такое объявление называют литералом объекта или литеральной нотацией.

Имя свойства объекта может быть какой-нибудь фразой. В этом случае ее записывают явно как строку в кавычках:

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 и через нее мы уже можем работать с этим объектом:

console.log(car.model);

Но, кроме того, обратите внимание, что в момент создания название ключа совпадает с названием переменной, которая ему присваивается. Это довольно частая ситуация в 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
};

И выполняя присваивание:

let lib = book;

будет скопирована ссылка на объект, а не сам объект.

То есть, и book и lib будут указывать на один и тот же объект. И мы можем дальше работать с объектом через любую переменную:

lib.title = "Муму";
console.log(book.title);

Здесь у вас может возникнуть вопрос: а как же можно создать клон объекта или делать его структурные копии? Об этом мы подробно поговорим на последующих занятиях, чтобы не перегружать информацией этот урок.

Пока вернемся к ссылкам на объект. Они приводят к некоторым особенностям взаимодействия с объектами. Например, задавая константную переменную:

const book = {};

мы тем не менее можем свободно менять свойства объекта:

book.nPages = 80;

Почему? Дело в том, что мы объявляем константой саму ссылку, а не сам объект, то есть, объект можно свободно менять, на ссылку это никак не влияет, но вот если мы захотим изменить ссылку:

book = null;

то получим сообщение об ошибке.

Следующий момент. Сравнивая между собой два объекта:

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 объекты преобразуются к примитивным типам данных, а потом сравниваются: либо как строки, либо как числа, либо как булевы значения и так далее.

Видео по теме