Объекты в JavaScript являются «двуличными». Они сочетают в себе два важных функционала.
Первый — это ассоциативный массив: структура, пригодная для хранения любых данных. В этой главе мы рассмотрим использование объектов именно как массивов.
Второй — языковые возможности для объектно-ориентированного программирования. Эти возможности мы изучим в последующих разделах учебника.
Ассоциативные массивы
Ассоциативный массив — структура данных, в которой можно хранить любые данные в формате ключ-значение.
Ее можно легко представить как шкаф с подписанными ящиками. Все данные хранятся в ящичках. По имени можно легко найти ящик и взять то значение, которое в нем лежит.
В отличие от реальных шкафов, в ассоциативный массив можно в любой момент добавить новые именованные «ящики» или удалить существующие. Далее мы увидим примеры, как это делается.
Кстати, в других языках программирования такую структуру данных также называют «словарь» и«хэш».
Создание объектов
Пустой объект («пустой шкаф») может быть создан одним из двух синтаксисов:
Обычно все пользуются синтаксисом (2), т.к. он короче.
Операции с объектом
Объект может содержать в себе любые значения, которые называются свойствами объекта. Доступ к свойствам осуществляется по имени свойства (иногда говорят «по ключу»).
Например, создадим объект person для хранения информации о человеке:
Основные операции с объектами — это:
- Присвоение свойства по ключу.
- Чтение свойства по ключу.
- Удаление свойства по ключу.
Для обращения к свойствам используется запись «через точку», вида объект.свойство:
Следующая операция:
- Проверка существования свойства с определенным ключом.
Например, есть объект person, и нужно проверить, существует ли в нем свойство age.
Для проверки существования есть оператор in. Его синтаксис: "prop" in obj, причем имя свойства — в виде строки, например:
4 |
alert("Свойство age существует!"); |
Впрочем, чаще используется другой способ — сравнение значения с undefined.
Дело в том, что в JavaScript можно обратиться к любому свойству объекта, даже если его нет. Ошибки не будет.
Но если свойство не существует, то вернется специальное значение undefined:
Таким образом мы можем легко проверить существование свойства — получив его и сравнив сundefined:
Есть два средства для проверки наличия свойства в объекте: первое — оператор in, второе — получить его и сравнить его с undefined.
Они почти идентичны, но есть одна небольшая разница.
Дело в том, что технически возможно, что свойство есть и равно undefined:
…При этом, как видно из кода, при простом сравнении наличие такого свойства будет неотличимо от его отсутствия.
Но оператор in гарантирует правильный результат:
Как правило, в коде мы не будем присваивать undefined, чтобы корректно работали обе проверки. А в качестве значения, обозначающего неизвестность и неопределенность, будем использовать null.
Доступ через квадратные скобки
Существует альтернативный синтаксис работы со свойствами, использующий квадратные скобкиобъект['свойство']:
В квадратные скобки можно передать переменную:
obj[key] использует значение переменной key в качестве имени свойства:
Записи person['age'] и person.age идентичны. С другой стороны, если имя свойства хранится в переменной (var key = "age"), то единственный способ к нему обратиться — квадратные скобкиperson[key].
Обращение через точку используется, если мы на этапе написания программы уже знаем название свойства. А если оно будет определено по ходу выполнения, например, введено посетителем и записано в переменную, то единственный выбор — квадратные скобки.
- На имя свойства при доступе «через точку» наложены синтаксические ограничения — примерно те же, что и на имя переменной. Оно не может начинаться с числа, содержать пробелы и т.п.
- При обращении через квадратные скобки можно использовать любую строку.
Например:
person['день рождения!'] = '18.04.1982'; |
В обоих случаях, имя свойства обязано быть строкой. Если использовано значение другого типа — JavaScript приведет его к строке автоматически.
Объявление со свойствами
Объект можно заполнить значениями при создании, указав их в фигурных скобках:{ ключ1: значение1, ключ2: значение2, ... }.
Такой синтаксис называется литеральным (оригинал - literal), например:

Следующие два фрагмента кода создают одинаковый объект:
10 |
menuSetup.width = 300; |
11 |
menuSetup.height = 200; |
12 |
menuSetup.title = 'Menu'; |
Названия свойств можно перечислять в кавычках или без, если они удовлетворяют ограничениям для имен переменных.
Например:
Важность: 3
3 |
user.surname = "Петров"; |
[Открыть задачу в новом окне]
Вложенные объекты в объявлении
Значением свойства может быть объект:
13 |
alert( user.size.top ) |
Здесь значением свойства size является объект {top: 90, middle: 60, bottom: 90 }.
Для целей отладки иногда хочется вывести объект целиком. В Firefox для этого существует нестандартный метод toSource.
В других браузерах его нет, но объект можно посмотреть через инструменты разработчика: отладчик или console.log.
Перебор свойств и значений
Для перебора всех свойств из объекта используется цикл по свойствам for..in. Это специальная синтаксическая конструкция, которая работает не так, как обычный цикл for (i=0;i<n;i++).
Синтаксис:
При этом в key будут последовательно записаны имена свойств.
Переменную можно объявить прямо в цикле:
Так иногда пишут для краткости кода.
Например:
Конечно, вместо key может быть любое другое имя переменной.
Количество свойств в объекте
Одного метода, который вернул бы количество свойств нет. Кросс-браузерный способ — это сделать цикл по свойствам и посчитать:
1 |
function getKeysCount(obj) { |
А вот так выглядит функция проверки на «пустоту»:
1 |
function isEmpty(obj) { |
Порядок перебора свойств
Упорядочены ли свойства в объектах? В теории, т.е. по спецификации — нет. На практике, все сложнее.
Браузеры придерживаются важного соглашения о порядке перебора свойств. Оно, хоть и не прописано в стандарте, но достаточно надежно, т.к. много существующего кода зависит от него.
- Браузеры IE<9, Firefox, Safari перебирают ключи в том же порядке, в котором свойства присваивались.
- Opera, современный IE, Chrome гарантируют сохранение порядка только длястроковых ключей. Численные ключи сортируются и идут до строковых.
То есть, если свойства в объекте строковые, то такой объект упорядочен. Свойства будут перебираться в порядке их присвоения:
Рассмотрим теперь случай, когда ключи «численные». Как мы знаем, у объектов в JavaScript ключи могут быть только строками, но если строка имеет вид целого числа, то некоторые интерпретаторы распознают это и обрабатывают такие ключи по-другому.
Например, рассмотрим объект, который задает список опций для выбора страны:
Мы хотим вывести эти опции посетителю в том же порядке, то есть «Танзания» в начале, а «США» — в конце (это если наш офис находится в Занзибаре, тогда такой порядок будет наиболее актуален).
Однако, при переборе их через for..in некоторые браузеры (IE9+, Chrome, Opera) выведут ключи не в том порядке, в котором они заданы, а в отсортированном порядке:
Нарушение порядка возникло, потому что ключи численные. Чтобы его обойти, можно применить небольшой хак, который заключается в том, что все ключи искусственно делаются строчными. Например, добавим им дополнительный первый символ '+'.
В этом случае браузер сочтет ключи строковыми и сохранит порядок перебора:
Передача по ссылке
Обычные значения: строки, числа, булевы значения, null/undefined копируются «по значению».
Это означает, что если в переменной message хранится значение "Привет", и ее скопировалиphrase = message, то значение копируются, и у нас будут две переменные, каждая из которых хранит значение: "Привет".
Объекты присваиваются и передаются «по ссылке».
В переменной хранится не сам объект, а ссылка на его место в памяти. Чтобы лучше понять это — сравним с обычными переменными.
Например:

А вот как выглядит переменная user = { name: "Вася" }.
Внимание: объект — вне переменной. В переменной — лишь ссылка («адрес места в памяти, где лежит объект»).

При копировании переменной с объектом — копируется эта ссылка, а объект по-прежнему остается в единственном экземпляре.
Получается, что несколько переменных ссылаются на один и тот же объект:
var user = { name: "Вася" }; |

Так как объект всего один, то в какой бы переменной его не меняли — это отражается на других:
Еще одна аналогия: переменная, в которую присвоен объект, на самом деле хранит не сами данные, а код к сейфу, где они хранятся.
При передаче ее в функцию, в локальные переменные копируется именно этот код, так что переменная может вносить в данные изменения.
Итак, при передаче объекта куда-либо, копируется лишь ссылка на него.
Чтобы скопировать сами данные, нужно достать их из объекта и скопировать на уровне примитивов.
Примерно так:
Важность: 5
[Открыть задачу в новом окне]
Компактное представление объектов
Эта секция относится ко внутреннему устройству структуры данных и требует специальных знаний. Она не обязательна к прочтению.
Объект, в таком виде как он описывается — занимает много места в памяти, например:
Здесь содержится информация о свойстве
name и его строковом значении, а также о свойстве
age и его численном значении. Представим, что таких объектов много.
Получится, что информация об именах свойств name и age дублируется в каждом объекте.
Чтобы избежать этого, браузеры используют специальное «компактное представление объектов».
При создании множества объектов одного и того же вида (с одинаковыми полями) интерпретатор запоминает, сколько у него полей и какие они, и хранит эти данные в отдельной структуре. А сам объект — в виде непрерывного массива данных.
Например, для объектов вида {name: "Вася", age: 25} будет создана структура, которая описывает данный вид объектов: «строка name, затем целое age», а сами объекты будут представлены в памяти данными: Вася25. Причем вместо строки обычно будет указатель на нее.
Браузер для доступа к свойству узнает из структуры, где оно лежит и перейдет на нужную позицию в данных. Это очень быстро, и экономит память, когда на одно описание структуры приходится много однотипных объектов.
Что же происходит, если к объекту добавляется новое поле? Например:
В этом случае браузер смотрит, есть ли уже структура, под которую подходит такой объект («строкаname, целое age, логическое admin»). Если нет — она создается. Изменившиеся данные объекта привязываются к ней.
Детали применения и реализации этого способа хранения варьируются от браузера к браузеру. Применяются дополнительные оптимизации.
Более подробно внутреннем устройстве типов вы можете узнать, например, из презентации Know Your Engines (Velocity 2011).
Итого
Объекты — это ассоциативные массивы с дополнительными возможностями:
- Доступ к элементам осуществляется:
- Напрямую по ключу
obj.prop = 5
- Через переменную, в которой хранится ключ:
- Удаление ключей:
delete obj.name.
- Цикл по ключам:
for (key in obj), порядок перебора соответствует порядку объявления для строковых ключей, а для числовых - зависит от браузера.
- Существование свойства может проверять оператор
in: if ("prop" in obj), как правило, работает и просто сравнение if (obj.prop !== undefined).
- Переменная хранит не сам объект, а ссылку на него. Копирование такой переменной и передача ее в функцию дублируют ссылку, но объект остается один.
Комментарии
Оставить комментарий
Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)
Термины: Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)