Объекты как ассоциативные массивы

Объекты в JavaScript сочетают в себе два важных функционала.

Первый – это ассоциативный массив: структура, пригодная для хранения любых данных. В этой главе мы рассмотрим использование объектов именно как массивов.

Второй – языковые возможности для объектно-ориентированного программирования. Эти возможности мы изучим в последующих разделах учебника.

Ассоциативные массивы

Ассоциативный массив – структура данных, в которой можно хранить любые данные в формате ключ-значение.

Её можно легко представить как шкаф с подписанными ящиками. Все данные хранятся в ящичках. По имени можно легко найти ящик и взять то значение, которое в нём лежит.

В отличие от реальных шкафов, в ассоциативный массив можно в любой момент добавить новые именованные «ящики» или удалить существующие. Далее мы увидим примеры, как это делается.

Кстати, в других языках программирования такую структуру данных также называют «словарь» и «хэш».

Создание объектов

Пустой объект («пустой шкаф») может быть создан одним из двух синтаксисов:

1. o = new Object();
2. o = {}; // пустые фигурные скобки

Обычно все пользуются синтаксисом (2), т.к. он короче.

Операции с объектом

Объект может содержать в себе любые значения, которые называются свойствами объекта. Доступ к свойствам осуществляется по имени свойства (иногда говорят «по ключу»).

Например, создадим объект person для хранения информации о человеке:

var person = {}; // пока пустой

Основные операции с объектами – это создание, получение и удаление свойств.

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

// при присвоении свойства в объекте автоматически создаётся "ящик"
// с именем "name" и в него записывается содержимое 'Вася'
person.name = 'Вася';

person.age = 25; // запишем ещё одно свойство: с именем 'age' и значением 25

Значения хранятся «внутри» ящиков. Обратим внимание – любые значения, любых типов: число, строка – не важно.

Чтобы прочитать их – также обратимся через точку:

alert( person.name + ': ' + person.age ); // "Вася: 25"

Удаление осуществляется оператором delete:

delete person.age;

Осталось только свойство name:

Иногда бывает нужно проверить, есть ли в объекте свойство с определенным ключом.

Для этого есть особый оператор: "in".

Его синтаксис: "prop" in obj, причем имя свойства – в виде строки, например:

if ("name" in person) {
  alert( "Свойство name существует!" );
}

Впрочем, чаще используется другой способ – сравнение значения с undefined.

Дело в том, что в JavaScript можно обратиться к любому свойству объекта, даже если его нет.

Ошибки не будет.

Но если свойство не существует, то вернется специальное значение undefined:

var person = {};

alert( person.lalala ); // undefined, нет свойства с ключом lalala

Таким образом мы можем легко проверить существование свойства – получив его и сравнив с undefined:

var person = {
  name: "Василий"
};

alert( person.lalala === undefined ); // true, свойства нет
alert( person.name === undefined ); // false, свойство есть.
Разница между проверками in и === undefined

Есть два средства для проверки наличия свойства в объекте: первое – оператор in, второе – получить его и сравнить с undefined.

Они почти идентичны, но есть одна небольшая разница.

Дело в том, что технически возможно, что свойство есть, а его значением является undefined:

var obj = {};
obj.test = undefined; // добавили свойство со значением undefined

// проверим наличие свойств test и заведомо отсутствующего blabla
alert( obj.test === undefined ); // true
alert( obj.blabla === undefined ); // true

…При этом, как видно из кода, при простом сравнении наличие такого свойства будет неотличимо от его отсутствия.

Но оператор in гарантирует правильный результат:

var obj = {};
obj.test = undefined;

alert( "test" in obj ); // true
alert( "blabla" in obj ); // false

Как правило, в коде мы не будем присваивать undefined, чтобы корректно работали обе проверки. А в качестве значения, обозначающего неизвестность и неопределенность, будем использовать null.

Доступ через квадратные скобки

Существует альтернативный синтаксис работы со свойствами, использующий квадратные скобки объект['свойство']:

var person = {};

person['name'] = 'Вася'; // то же что и person.name = 'Вася'

Записи person['name'] и person.name идентичны, но квадратные скобки позволяют использовать в качестве имени свойства любую строку:

var person = {};

person['любимый стиль музыки'] = 'Джаз';

Такое присвоение было бы невозможно «через точку», так интерпретатор после первого пробела подумает, что свойство закончилось, и далее выдаст ошибку:

person.любимый стиль музыки = 'Джаз'; // ??? ошибка

В обоих случаях, имя свойства обязано быть строкой. Если использовано значение другого типа – JavaScript приведет его к строке автоматически.

Доступ к свойству через переменную

Квадратные скобки также позволяют обратиться к свойству, имя которого хранится в переменной:

var person = {};
person.age = 25;
var key = 'age';

alert( person[key] ); // выведет person['age']

Вообще, если имя свойства хранится в переменной (var key = "age"), то единственный способ к нему обратиться – это квадратные скобки person[key].

Доступ через точку используется, если мы на этапе написания программы уже знаем название свойства. А если оно будет определено по ходу выполнения, например, введено посетителем и записано в переменную, то единственный выбор – квадратные скобки.

Объявление со свойствами

Объект можно заполнить значениями при создании, указав их в фигурных скобках: { ключ1: значение1, ключ2: значение2, ... }.

Такой синтаксис называется литеральным (англ. literal).

Следующие два фрагмента кода создают одинаковый объект:

var menuSetup = {
  width: 300,
  height: 200,
  title: "Menu"
};

// то же самое, что:

var menuSetup = {};
menuSetup.width = 300;
menuSetup.height = 200;
menuSetup.title = 'Menu';

Названия свойств можно перечислять как в кавычках, так и без, если они удовлетворяют ограничениям для имён переменных.

Например:

var menuSetup = {
  width: 300,
  'height': 200,
  "мама мыла раму": true
};

В качестве значения можно тут же указать и другой объект:

var user = {
  name: "Таня",
  age: 25,
  size: {
    top: 90,
    middle: 60,
    bottom: 90
  }
}

alert(user.name) // "Таня"

alert(user.size.top) // 90

Здесь значением свойства size является объект {top: 90, middle: 60, bottom: 90 }.

Компактное представление объектов

Hardcore coders only

Эта секция относится ко внутреннему устройству структуры данных. Она не обязательна к прочтению.

Браузер использует специальное «компактное» представление объектов, чтобы сэкономить память в том случае, когда однотипных объектов много.

Например, посмотрим на такой объект:

var user = {
  name: "Vasya",
  age: 25
};

Здесь содержится информация о свойстве name и его строковом значении, а также о свойстве age и его численном значении. Представим, что таких объектов много.

Получится, что информация об именах свойств name и age дублируется в каждом объекте. Чтобы этого избежать, браузер применяет оптимизацию.

При создании множества объектов одного и того же вида (с одинаковыми полями) интерпретатор выносит описание полей в отдельную структуру. А сам объект остаётся в виде непрерывной области памяти с данными.

Например, есть много объектов с полями name и age:

{name: "Вася", age: 25}
{name: "Петя", age: 22}
{name: "Маша", age: 19}
...

Для их эффективного хранения будет создана структура, которая описывает данный вид объектов. Выглядеть она будет примерно так: <string name, number age>. А сами объекты будут представлены в памяти только данными:

<структура: string name, number age>
Вася 25
Петя 22
Маша 19

При добавлении нового объекта такой структуры достаточно хранить значения полей, но не их имена. Экономия памяти – налицо.

А что происходит, если к объекту добавляется новое свойство? Например, к одному из них добавили свойство isAdmin:

user.isAdmin = true;

В этом случае браузер смотрит, есть ли уже структура, под которую подходит такой объект. Если нет – она создаётся и объект привязывается к ней.

Эта оптимизация является примером того, что далеко не всё то, что мы пишем, один-в-один переносится в память.

Современные интерпретаторы очень стараются оптимизировать как код, так и структуры данных. Детали применения и реализации этого способа хранения варьируются от браузера к браузеру. О том, как это сделано в Chrome можно узнать, например, из презентации Know Your Engines. Она была некоторое время назад, но с тех пор мало что изменилось.

Итого

Объекты – это ассоциативные массивы с дополнительными возможностями:

  • Доступ к элементам осуществляется:
    • Напрямую по ключу obj.prop = 5
    • Через переменную, в которой хранится ключ:
      var key = "prop";
      obj[key] = 5
  • Удаление ключей: delete obj.name.
  • Существование свойства может проверять оператор in: if ("prop" in obj), как правило, работает и просто сравнение if (obj.prop !== undefined).

Задачи

важность: 3

Мини-задача на синтаксис объектов. Напишите код, по строке на каждое действие.

  1. Создайте пустой объект user.
  2. Добавьте свойство name со значением Вася.
  3. Добавьте свойство surname со значением Петров.
  4. Поменяйте значение name на Сергей.
  5. Удалите свойство name из объекта.
var user = {};
user.name = "Вася";
user.surname = "Петров";
user.name = "Сергей";
delete user.name;
Карта учебника

Комментарии

перед тем как писать…
  • Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  • Для одной строки кода используйте тег <code>, для нескольких строк кода — тег <pre>, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)
  • Если что-то непонятно в статье — пишите, что именно и с какого места.