Объекты в 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)