☕ Учебник по JavaScript: работа с объектами
Статья охватывает все аспекты типа Object: создание, наследование, работа с полями и сериализация. Новички освоят базовые концепции языка, а продвинутые пользователи освежат в памяти знания.
Что такое Объекты в JavaScript
Объекты (object) – это особенный тип в JS. Остальные типы называются примитивными, потому что значения примитивных типов могут быть только простыми значениями, например, строка или число. В объектах хранятся коллекции или более сложные структуры.
Объект может быть создан с помощью фигурных скобок {...}
с необязательным списком свойств. Свойство – пара ключ-значение, где ключ – строка (имя свойства), а значение может быть чем угодно.
Сравним объект, например, с бейсболкой. У нее есть цвет, форма, вес, материал и т. д. Также и объект JS, содержит свойства, которые определяют его характеристики.
let cap = new Object(); //конструктор объекта let cap = new {}; //литерал объекта let cap = Object.create({}); //создание объекта определенное стандартом ECMAScript 5
Чаще используют вариант с фигурными скобками {..}
. Такое объявление называют литералом объекта или литеральной нотацией.
Литералы и свойства
Литерал объекта – заключенный в фигурные скобки список свойств (пар имя/значение) через запятую, например: (число(1), строка("строка"), объект({a:1, b:2}))
object.property
Имя объекта и имена его свойств чувствительны к регистру. Свойства могут определяться в момент указания их значений, а также с помощью скобочной записи. Неопределенные свойства объекта – undefined
, не null
. Например, создадим бейсболку и зададим ее свойства:
var cap = new Object(); cap.color = "Red"; cap["volume"] = 300;
Для удаления свойств можно использовать оператор delete
:
delete cap.color;
Имя свойства может состоять из нескольких слов, заключенных в кавычки:
let cap = { background: "Red", "has image": true }
Объекты, объявленные как константы (const
), не могут быть изменены. Свойства таких объектов можно менять.
Например:
const cap = { color: "Red" } cap.color = "Green"; // допустимо alert(cap.color); // Green cap = new Object(); // Ошибка
Ошибки доступа к свойствам
Как писалось выше, попытка обратиться к несуществующему свойству не является ошибкой – будет просто получено значение undefined
. Однако если обратиться к свойству несуществующего объекта, то это будет считаться ошибкой.
Пример:
var cap = { Color: "Red", Volume: 300 }; var a = cap.Text; // undefined var b = cap.Text.length; // Ошибка TypeError
Для того чтобы защититься от исключения, рассмотрим следующий пример:
// Наглядный и понятный вариант var b = undefined; if (cap) { if (cap.Text) { b = cap.Text.length; } } // Краткая форма var b = cap && cap.Text && cap.Text.length;
Запись значений в свойства, доступные только для чтения к исключению, как правило, не приводят.
Пример:
Object.prototype = {}; // исключения не будет, прототип не изменится
Когда запись значения свойству объекта будет неудачной:
- Объект имеет собственное свойство, доступное только для чтения.
- Объект имеет унаследованное свойство, доступное только для чтения.
- Объект не имеет ни собственного, ни унаследованного свойства, которое требует изменить атрибут объекта
extensible
и имеет значениеfalse
.
Квадратные скобки
Как описывалось выше, со свойствами можно работать с помощью квадратных скобок. Особенно это полезно для свойств, которые состоят из нескольких слов, например:
cap.has image = true; // ошибка cap["has image"] = true; // корректно
При обращении к свойствам объекта с помощью квадратных скобок, можно также задавать, получать и удалять свойства:
cap["color"] = "Red"; // присвоить значение свойству alert(cap["color"]); // получить значение свойства delete cap["color"]; // удалить свойство
Имя свойства могут быть выражениями и могут храниться в переменных, например:
let propertyName = "color"; cap[propertyName] = "Red";
Вычисляемые свойства
Квадратные скобки также используются в литеральной нотации для создания вычисляемого свойства: мы можем задать имя свойства через переменную и потом работать с ним. Например, добавим бейсболке какое-нибудь свойство:
let newProperty = prompt("Задайте имя нового свойства", "property"); let cap = { [newProperty]: 1, }; alert(cap.property);// 1 если newProperty = "property"
Если вы поэкспериментируете с примером выше и вместо property
зададите какое-нибудь другое значение, то результат будет undefined
.
Квадратные скобки позволяют больше, нежели точка: можно рассчитывать имена свойств во время выполнения кода или задать его извне, как в примере выше.
Свойство из переменной
Возникает необходимость использовать переменные как значения свойств с тем же именем.
Пример:
function makeCap(color, volume){ return { color: color, volume: volume }; } let cap = makeCap("Red", 500); alert(cap.color); // Red
В примере имена свойств color
и volume
совпадают с именами переменных, подставляемых в значения свойств. Существует синтаксический сахар для упрощенной записи.
Пример:
function makeCap(color, volume){ return { color, volume }; }
Можно и еще короче:
let cap = { color, volume: 500 };
Ограничения на имена свойств
В отличие от переменных, для свойств нет ограничений на именование, таких как for
, let
, return
и т. д.
let o = { for: "hello ", let: "proglib", return: ".io" }; alert(o.for + o.let + o.return); // hello proglib.io
Ограничений для свойств нет также и по типу, т. е. они могут быть заданы строкой или символом, а типы, кроме строк, будут преобразованы к строке.
Пример:
let o = { 1: "Тест" // аналогично "1": "Тест" }; alert(o["0"]); //Тест alert(o[0]); //Тест
Исключением является специальное свойство __proto__
. Ему нельзя установить не объектное значение:
let o = {}; o.__proto__ = 1; alert(o.__proto__); //[object Object]
В этом случае присвоение значения 1
игнорируется.
Проверка существования свойства – оператор in
JavaScript позволяет получить доступ к любому свойству, даже не существующему, в отличие от других языков – ошибки не будет.
При обращении к несуществующему свойству возвращается undefined
. Проверить несуществующее свойство можно следующим образом:
let cap = {}; alert(cap.color === undefined); //true – свойства нет
Для проверки существования свойства в объекте используется специальный оператор in
.
Пример:
let cap = { color: "Red", volume: 500 }; alert("volume" in cap); //true – свойство volume существует alert("weight" in cap); //false – свойство weight не существует
Слева от оператора in
указывается имя свойства. Если в кавычках, то это имя свойства. Если без кавычек, то это имя переменной, содержащей имя свойства.
Пример:
let cap = { color: "Red"}; let name = "color"; alert(name in cap); //true
Оператор in
позволяет определить, что свойство у объекта именно есть, а не то, что его значение undefined
.
Пример:
let cap = { color: undefined }; alert(cap.color); // undefined при использовании === было бы получено false alert("color" in cap); // true
Данный пример – скорее исключение, в связи с тем, что undefined
обычно не присваивают. Для пустых/неизвестных свойств используют null
.
Цикл for…in
Цикл for...in
используется для перебора всех свойств.
Пример:
let cap = { color: "Red", volume: 300, hasImage: true }; for (let name in cap) { alert(name); //color, volume, hasImage alert(cap[name]); //Red. 300, true }
В примере name
– переменная, объявленная внутри цикла for
.
Упорядочение свойств объекта
Свойства объекта упорядочены следующим образом: сначала идут целочисленные типы в порядке возрастания, остальные – в порядке создания.
Пример:
let a = { "b":2, 3:"4", 1:"b"}; for(let c in a) alert(a[c]); // b, 4, 2
Как видим в примере выше, сначала вывелись значения свойства 1
и 3
, а потом значение свойства b
.
Целочисленные свойства – такие свойства, которые могут быть преобразованы в число и обратно.
Пример:
alert(String(Math.trunc(Number("1")))); // "1" целочисленный alert(String(Math.trunc(Number("+1")))); // "1" != "+1" не целочисленное alert(String(Math.trunc(Number("1.2")))); // "1" != "1.2" не целочисленное
Создание объектов в JavaScript
Создание объектов с помощью оператора new
Оператор new
используется для создания и инициализации нового объекта. После оператора new
указывается имя конструктора (функции, создающей объект).
Примеры встроенных конструкторов:
var obj = new Object(); // {} var arr = new Array(); // [] var date = new Date(); // объект Date
Также можно создать свой конструктор для инициализации своих объектов.
Пример:
var cap = Object.create({color:"Red", volume: 300});
Пример:
var obj = Object.create(Object.prototype);
var obj = Object.create(null);
Прототипы
Прототип – объект, на основании которого создается другой объект, наследующий его свойства.
Наследование
Наследование – в JavaScript происходит от объекта прототипа. Для наследования свойств одного объекта другим применяется функция inherit()
.
При обращении к свойству объекта, поиск свойства сначала происходит по самому объекту. Если у объекта такого свойства нет, то производится попытка поиска в прототипе, далее в прототипе прототипа и так далее до тех пор, пока не будет найдено свойство, либо не достигнет прототипа, у которого нет прототипа.
Пример:
var a = {}; // Прототипом объекта является Object.prototype a.propA = 1; // Дадим ему собственное свойство propA var b = inherit(a); //b наследует свойства a и Object.prototype b.propB = 2; // Дадим ему собственное свойство propB var c = inherit(b); // c наследует свойства a, b и Object.prototype с.propC = 3; // Дадим ему собственное свойство propC var str = c.toString(); // toString наследуется от Object.prototype var sum = c.propA + c.propB; // 3 propA и propB наследуются от a и b
При присваивании значений свойству объекта проверяется, имеется ли это свойство у объекта или оно унаследовано. При наличии собственного свойства происходит запись значения, а при наличии унаследованного свойства происходит скрытие унаследованного свойства свойством с тем же именем.
При присвоении значения будут проверены все свойства в цепочке прототипов на допустимость. При наследовании свойств только для чтения, присвоение значения производиться для таких свойств не будет.
Пример:
var a = { prop:1 }; var b = inherit(a); // наследует свойство prop b.x = 1; b.y = 1; // определение собственных свойств b.prop = 2; // переопределение унаследованного свойства alert(a.prop); // 1, объект-прототип остался неизменным
Атрибуты объекта
Объекты JavaScript имеют атрибуты prototype
, class
, extensible
.
Атрибут prototype
Как уже рассматривалось выше, prototype
объекта определяет объект, от которого наследуются свойства. Данный атрибут устанавливается в момент создания объекта.
Атрибут class
Пример:
function classof(obj){ if (obj === null) return "null"; if (obj === undefined) return "undefined"; return Object.prototype.toString.call(obj); }
Функция из примера позволяет определить класс объекта.
Атрибут extensible
Данный атрибут отвечает за возможность добавлять объекту новые свойства. В ECMAScript 3 встроенные и определяемые пользователем объекты неявно допускали возможность расширения, расширяемость объектов среды выполнения определялась конкретной реализацией. В ECMAScript 5 встроенные и определяемые пользователем объекты являются расширяемыми до тех пор, пока не преобразованы в нерасширяемые объект. С объектами среды выполнения ситуация осталась той же.
Пример:
var cap = { color: "Red", volume: 300 }; var json = JSON.stringify(cap); // json == '{"color":"Red","volume":300}' var obj = JSON.parse(json); // Копия объекта cap
Формат JSON, к сожалению, не может применяться для представления всех возможных значений. Вот некоторые примечание касательно преобразуемых типов:
- Объекты, массивы, строки, конечные числовые значения,
true
,false
поддерживают двустороннее преобразование. NaN
,Infinity
,-Infinity
преобразуются вnull
.Date
преобразуются в строки с датами формата ISO, однако, обратно они восстанавливаются в виде строк.Function
,RegExp
,Error
,undefined
– не могут быть сериализованы или восстановлены.