☕ Учебник по 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 – не могут быть сериализованы или восстановлены.
***

Материалы по теме

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

eFusion
01 марта 2020

ТОП-15 книг по JavaScript: от новичка до профессионала

В этом посте мы собрали переведённые на русский язык книги по JavaScript – ...
admin
10 июня 2018

Лайфхак: в какой последовательности изучать JavaScript

Огромный инструментарий JS и тонны материалов по нему. С чего начать? Расск...