Найти в Дзене
Репетитор IT mentor

Объекты в JavaScript: всё что нужно знать начинающему

Оглавление

Объект — фундаментальный тип данных JavaScript. Объект чем-то похож на ассоциативный массив, который представляет собой составное значение: он состоит из нескольких значений (примитивов, функций и других объектов) и позволяет сохранять и извлекать эти значения и объекты по именам (ключам).

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

Такая привязка строк к значениями представляет собой структуру данных, которую можно называть: "хеш", "хеш-таблица", "словарь", "ассоциативный массив". Но объект — это не только набор пар "имя-значение". Кроме собственного набора свойств, объект в JavaScript может наследовать свойства другого объекта, который называется прототипом. Методы объекта обычно являются унаследованными свойствами. Наследование прототипов — ключевая особенность JS.

Объекты в JS являются динамическими. То есть мы можем изменять свойства объекта: добавлять и удалять их. В JavaScript также можно имитировать статические объекты или структуры, которые используются в статически типизированных языках программирования. Можно даже просто использовать объекты в качестве набора строк, игнорируя значения, ассоциируемые с ними.

Любое значение JavaScript, которое не является строкой, числом или значением true, false, null, undefined, является объектом.

Правда здесь есть один интересный момент, баг JS, который оставили для обратной совместимости. Если вы выведите typeof(null), то JavaScript вернет вам object.

Объекты можно изменять. Как правило, это сводится к изменению ссылок на них. Например, если переменная x ссылается на объект и в коде выполняется инструкция var y = x; , то данная инструкция присваивает переменной y ссылку на этот же объект, а не на его копию. Это важно запомнить! Потому что любые изменения, сделанные через переменную y, будут видны через переменную x.

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

Объекты можно создавать с помощью объектных литералов, ключевого слова new и функции Object.create().

1. Объектные литералы

Проще всего создать объект, включив в код объектный литерал — это список разделенных запятой пар "имя : значение", заключенный в фигурные скобки. Имя свойства и значение отделены друг от друга двоеточием. Имя свойства представляет собой идентификатор или строковый литерал (допустима пустая строка). Значение свойства может быть любое выражение JavaScript. Значение выражения (это может быть примитивное или объектное значение) становится значением свойства. Ниже приведем ряд примеров.

-2

Обратите внимание, что имена свойств, не являющихся идентификаторами, должны быть заключены в двойные кавычки.

В именах-литералах разрешены пробелы, ключевые слова и знаки пунктуации. Так как это прежде всего строки в кавычках.

2. Ключевое слово new

Оператор new создает и инициализирует новый объект. После ключевого слова new должен находиться вызов функции. Функция, которая используется вместе с оператором new, называется конструктором. Конструктор-функция служит для инициализации создаваемого объекта. Базовая библиотека JavaScript содержит встроенные конструкторы всех встроенных типов данных. Ниже приведем ряд примеров создания встроенных типов.

-3

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

3. Прототипы

Чтобы понять третью методику создания объектов, нужно понять что такое прототип. Начнем с того, что с каждым объектом в JavaScript ассоциирован другой объект JavaScript (иногда null, но редко). Этот объект, являющийся ассоциацией первого, называется прототипом, а первый объект наследует свойства прототипа. То есть прототип для объекта подобен родителю, который отдает своему детищу весь свой функционал. А "ребенок" уже может иметь дополнительный функционал, которого ранее не было у родителя-прототипа.

Все объекты, созданные объектными литералами, имеют один и тот же прототип, на который можно ссылаться как на Object.prototype. Объекты, созданные с помощью ключевого слова new и конструктора, имеют в качестве своего прототипа значения свойства prototype. Например, объект new Object() имеет прототип Object.prototype, как и объект, созданный с помощью литерала {}. Аналогично объект new Arra() имеет прототип Array.prototype, а объект new Date() — прототип Date.prototype.

Объект Object.prototype — один из самых редких объектов, не имеющих прототипа и не наследующих никаких свойств. То есть он подобен первоисточнику, корню дерева объектов языка. Другие же объекты имеют прототипы. Также другие объекты прототипов - обычные объекты, имеющие свои прототипы.

Все встроенные и большинство пользовательских конструкторов имеют прототипы, наследуемые от Object.prototype. Например, Date.prototype наследует свойства Object.prototype. Поэтому объект Data, созданный через new Date(), наследует свойства как Date.prototype, так и Object.prototype. Этот связанный ряд объектов прототипов называется цепочкой прототипов.

Если прототип не указан, то по умолчанию будет Object.prototype, а если указан, то будет прототип указанный объект (но он в свою очередь всё равно будет иметь прототип Object.prototype и замыкать цепочку).

-4

4. Функция Object.create()

В ECMAScript 5 определена функция Object.create(), которая создает объект, используя первый аргумент в качестве прототипа объекта. Кроме того, функция Object.create() принимает необязательный аргумент, описывающий свойства нового объекта.

Object.create() — статическая функция, а не метод, вызываемый через индивидуальные объекты. Ей нужно передать прототип объекта.

var o1 = Object.create({x : 1, y : 2}); — Объект o1 наследует свойства x и y

Можно передать значение null, но тогда новый объект не будет иметь прототипа и не унаследует ничего, даже базового метода toString(), а это значит, что объект не будет работать с оператором "+".

var o2 = Object.create(null); — Объект o2 НЕ наследует ни свойств, ни методов.

Если нужно создать обычный пустой объект, такой, который делает new Object() или литералы в виде двух фигурных скобок {}, то можно передать функции Object.create() прототип Object.prototype.

var o3 = Object.create(Object.prototype); // аналог {} или new Object()

Возможность создать объект с произвольным прототипом, то есть создать "наследника" любого объекта, является мощным средством ECMAScript 5. В листинге ниже приведу пример как можно имитировать это наследование средствами ECMAScript 3. Метод получается длиннее, чем одна строчка :)

Создание объекта, наследующего прототип, в случае если Object.create() не поддерживается.

Пример реализации наследования прототипа, если не определена Object.create()   https://jsfiddle.net/1s8qdenv/
Пример реализации наследования прототипа, если не определена Object.create() https://jsfiddle.net/1s8qdenv/

Функция inherit() возвращает объект, наследующий свойства прототипа p. В нем используется введенная в ECMAScript 5 функция Object.create(), если она определена. А если она не определена, то применяется устаревшая методика.

Свойства - наиболее важная часть объекта

Чтение и запись свойств в объект. Получить значение свойства можно с помощью оператора точки . для идентификатора или скобок [] для строкового литерала. Слева от точки или скобок должно быть выражение, вычисленное значение которого дает объект.
1. При использовании точки, справа от нее должен быть идентификатор, обозначающий имя свойства.
2. При использовании квадратных скобок, в них должно быть выражение, возвращающее число или строку с именем свойства.

-6

Свойства можно добавлять/создавать у уже готовых объектов. Для создания или записи свойства используется также точка или квадратные скобки, как и для чтения. Отличие только в том, что они находятся слева от оператора присваивания.

-7

Наследование свойств

Объекты в JavaScript имеют набор "собственных свойств" и, кроме того, наследуют набор свойств от своих прототипов. Попытаемся понять эту концепцию, рассматривая способы обращения к свойствам. Будем использовать нашу функцию inherit(), которая создает объекты на основе заданных прототипов.

Предположим, что нужно прочитать свойство x у объекта obj. Если у объекта obj нет собственного свойства с именем x, то выполняется его поиск в прототипе. Если прототипе нет собственного свойства с именем x, но есть свой прототип, то начинается поиск свойства x в прототипе прототипа. Этот процесс повторяется до тех пор, пока в этой цепочке не будет найдено свойство x или не найдется прототип, у которого не будет прототипа, то есть объект свойство prototype которого будет иметь значение null.

Таким образом, атрибут прототипа создает цепочку, похожую на однонаправленный связанный список прототипов, из которых наследуются свойства. Тут важно отметить, что в JavaScript методы объекта также считаются свойствами.

-8

Допустим, нужно присвоить некоторое значение свойству x объекта obj. Если obj имеет собственное (не унаследованное от прототипа) свойство с именем x, то операция присвоения всего лишь изменяет значение существующего свойства. В противном случае оператор присваивания создает еще одно свойства объекта obj с именем x.

Если же объект obj уже имеет унаследованное свойств, то это унаследованной свойство скрывается вновь созданным собственным свойством с таким же именем. То есть приоритет собственных свойств всегда выше, чем у унаследованных свойств.

Удаление свойств

Оператор delete удаляет свойство из объекта. Единственный операнд данного оператора должен быть выражением, которое дает доступ к свойству объекта. Оператор delete удаляет не значение свойства, а само свойство.

-9

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

Проверка свойств

Итак, любой объект в JS представляет собой набор свойств. Часто полезно проверить, что содержится в этом наборе, если в объекте свойство с заданным именем и какое это свойство. Это можно сделать с помощью оператора in и методов hasOwnProperty() и propertyIsEnumerable(). Еще один способ проверки состоит в чтении свойств и анализе полученного результата.

Оператор in принимает строку с именем свойства с левой стороны и объект с правой стороны. Если объект имеет собственное или унаследованное свойство с этим именем, оператор in возвращает true.

toString() - true т.к. унаследован, x - нет и не наследуется в цепочке прототипов, autor и pages - есть явно в объекте.
toString() - true т.к. унаследован, x - нет и не наследуется в цепочке прототипов, autor и pages - есть явно в объекте.

Принадлежащий объекту метод hasOwnProperty() проверяет, имеет ли объект собственное свойство с заданным именем. Если свойство с этим именем унаследованное, то метод возвращает false.

toString() - false потому что унаследованное свойство. y - нет в объекте, x - есть в объекте
toString() - false потому что унаследованное свойство. y - нет в объекте, x - есть в объекте

Если собственное свойство с заданным именем существует, то с помощью метода propertyIsEnumerable() можно проверить, является ли оно перечислимым. Этот метод возвращает true, если указанное свойство собственное и атрибут enumerable равен true. Некоторые встроенные свойства неперечислимые. Свойства, созданные обычным кодом JavaScript, являются перечислимыми, если не задано обратное с помощью методов, определенных в спецификации ECMAScript 5.

Метод toString() неперечисляемый
Метод toString() неперечисляемый

Вместо использования оператора in обычно можно выполнить следующее: сначала прочитать значение свойства, а затем с помощью оператора !== проверить, определено ли оно.

var o = { x : 1 };
o.x !== undefined; // true
o.y !== undefined; // false
o.toString !== undefined; // true

Важное(!): Есть одна операция, которую можно выполнить только с помощью оператора in, но не с помощью приведенного выше способа ( чтение и проверка на undefined оператором !== ). Оператор in отличает несуществующие свойства от свойств, которые существуют, но имеют значение undefined.

Пример:
var o = { x : undefined };
o.x !== undefined; // false: свойство существует, но не определено
o.y !== undefined; // false: свойство не существует
"x" in o; // true: свойство существует
"y" in o; // false: свойство не существует
delete o.x; // Удаление свойства x
"x" in o; // false: теперь свойства x нет

Перечисление свойств

Часто нужно работать не с индивидуальными свойствами, а пройти по всем свойствам объекта автоматически или получить их список. Обычно это делают с помощью цикла for/in, но спецификация ECМAScript 5 предоставляет два удобных альтернативных способа.

Тело цикла for/in выполняется по одному разу для каждого перечислимого свойства заданного объекта, причем имя свойства присваивается переменной цикла. Унаследованные объектом встроенные методы не являются перечислимыми. Но перечислимыми, кроме прочего, являются свойства, добавленные кодом в объект (если в этом же коде не указано обратное). Рассмотрим пример, в котором объект о имеет три перечислимых собственных свойства.

-13

В большинстве библиотек новые методы и свойства добавляются в Object.рrototype таким образом, что они наследуются и доступны для всех объектов. Однако до ECМAScript 5 не было способа сделать добавляемые методы неперечислимыми, поэтому все они перечислялись в цикле for/in. Для решения этой проблемы можно фильтровать свойства, возвращаемые объектом цикла for / in.

Ниже приведены два способа фильтрации.

Способ пропустить несобственные свойства
Способ пропустить несобственные свойства
Способ пропустить функции
Способ пропустить функции

Сериализация свойств и объектов в JavaScript

Сериализция - это процесс преобразования состояния объекта в строку, из которой позже можно будет восстано­вить состояние. Спецификация ECМAScript 5 определяет две функции, предназначенные для сериализации и восстановления объектов JavaScript: JSON.stringify() и JSON.раrse(). В этих функциях используется формат обмена данными JSОN ( JavaScript Object Notation - зaпиcь объектов JavaScript, http://json.org ) . Синтаксис JSON напоминает синтаксис литералов объектов и мaccивoв JavaScript.

-16

Обратите внимание на то, что синтаксис JSON является подмножеством синтаксиса JavaScript и поэтому не представляет всех значений JavaScript. Поддерживаются и могут быть восстановлены объекты, массивы, строки, конечные числа и значения true , false и null.

Методы чтения и записи свойств

Свойство содержит имя и значение. В ECМAScript 5 (и в последних реализациях ECМAScript 3 во всех основных браузерах, кроме IE} значение может быть заменено одним или двумя методами , которые называются метод чтения свойства (getter) и метод записи свойства (setter) .

Свойства, определенные методами чтения и записи (они называются свойствами с методами доступа) , необходимо отличать от простых свойств, которые являются всего лишь значениями, напоминающими поля объектов в других объектно-ориентированных языках.

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

-17

Свойства с методами доступа определяются как одна или две функции, имена которых совпадают с именем свойства, но ключевое слово function заменено словом get или set . Обратите внимание на то, что для отделения имени свойства от метода доступа к свойству двоеточие не используется. После тела функции необходима запятая для отделения одного метода от другого.

Пример: В качестве примера рассмотрим следующий объект, представляющий точку в двухмерном декартовом пространстве. Обычные свойства (без методов доступа) содержат координаты х и у точки, а свойства с методами доступа - эквивалентные полярные координаты точки.

-18

Приведем еще один пример полезного объекта, имеющего свойство с методами доступа.

-19

Атрибуты свойств

Кроме имен и значений, у свойств есть атрибуты, которые определяют, являются ли свойства записываемыми, перечислимыми и конфигурируемыми. В ECМAScript 3 способа установки или изменения атрибутов не существует, все свойства являются записываемыми, перечислимыми и конфигурируемыми. Рассмотрим функции ECМAScript 5, позволяющие читать и устанавливать атрибуты свойств.

В данном разделе мы будем считать методы доступа к свойствам атрибутами свойств. Согласно этой логике можно даже сказать, что значение свойства без методов доступа является атрибутом, и мы так и будем полагать в данном разделе.

Следовательно, мы будем считать, что у свойства без методов доступа есть имя и четыре атрибута:

1. значение
2. доступность для записи
3. перечислимость
4. конфигурируемость

Свойства с методами доступа не имеют атрибутов "значение" и "доступность для записи". Их доступность для записи определяется наличием или отсутствием метода записи свойства. Следовательно , у свойств с методами доступа есть четыре атрибута: метод чтения, метод записи, перечислимость и конфигурируемость.

В определенных в ECМAScript 5 методах чтения и установки атрибутов свойств используется объект, который называется дескриптором свойства и представляет набор из четырех атрибутов. Объект дескриптора свойства имеет четыре свойства с теми же именами, что и названия атрибутов представляемого свойства. Таким образом, объект дескриптора свойства без методов доступа имеет свойства value (значение) , writаblе (доступный для записи), enumerable ( перечислимый) и configurable ( конфигурируемый). Дескриптор свойства с методами доступа имеет свойства get и set вместо value и writаblе. Свойства writаblе, enumerable и configurable являются булевыми значениями , а свойства get и set - функциями.

Чтобы получить дескриптор именованного свойства заданного объекта, нужно вызвать метод Object.getOwnPropertyDescriptor().

Маленькие объекты, которые содержат в себе семантику, называются дескрипторами.

-20

Как видно из имени, метод Object.getOwnPropertyDescriptor() работает только с собственными свойствами. Для чтения атрибутов унаследованных свойств нужно явно пройти по цепочке прототипов.

Для установки атрибутов свойства или создания свойства с заданными атрибутами вызовите метод Object.defineProperty() и передайте ему изменяемый объект, имя изменяемого или создаваемого свойства и объект дескриптора свойства.

  • writable — значение свойства может быть изменено, используется только для дескрипторов данных.
  • configurable — тип свойства может быть изменён или свойство может быть удалено.
  • enumerable — свойство используется в общем перечислении.
    Дескрипторы данных таковы, что определяют конкретное значение, которое соответствует дополнительному
    value - параметру, описывающему конкретные данные, привязанные к свойству.
  • value — значение свойства
-21

Дескриптор свойства, передаваемый методу Object.defineProperty() , необязательно должен содержать все четыре атрибута. При создании свойства опущенные атрибуты принимают значения false или undefined. При модификации существующего свойства опущенные атрибуты просто остаются прежними. Не забывайте, что данный метод изменяет или создает только собственные свойства, а с унаследованными свойствами он не работает.

С помощью метода Object.defineProperties() можно создать или изменить более одного свойства за раз. Первый аргумент содержит модифицируемый объект, а второй аргумент - объект (дескриптор), отображающий имена создаваемых и модифицируемых свойств на дескрипторы этих свойств.

-22

Ранее мы рассматривали метод Object.create(), определенный в ECМAScript 5. Первым аргументом этого метода должен быть объект прототипа создаваемого объекта. Данный метод принимает также второй аргумент, совпадающий с вторым аргументом метода Object.defineProperties(). Если передать набор дескрипторов свойств методу Object.create() , они будут применены для добавления свойств в создаваемый объект.

Атрибуты объекта

У каждого объекта есть атрибугы прототипа, класса и расширяемости. Рассмотрим основные.

prototype

Атрибут прототипа задает объект, от которого данный объект наследует свойства. Этот атрибут устанавливается при создании объекта. Как было указано в предыдущих разделах, объекты , созданные с помощью объектных литералов, получают в качестве прототипа объект Object.prototype. Объекты, созданные с помощью ключевого слова new, в качестве прототипа имеют свойство prototype функции конструктора. Объекты , созданные с помощью метода Object.create(), получают в качестве прототипа первый аргумент (он может быть равен null ) .

В реализациях ECМAScript 5 можно прочитать прототип любого объекта, передав данный объект методу Object.getPrototypeOf() . В ECМAScript 3 нет эквивалентного метода, но иногда можно получить прототип объекта о с помощью выражения о.constructor.рrototype.

Чтобы выяснить, является ли один объект прототипом (или элементом цепочки прототипов) другого объекта, используйте метод isPrototypeOf() . Например, чтобы узнать, является ли р прототипом объекта о, запишите выражение р.isPrototypeOf(o) , как в следующем коде.

-23

Обратите внимание на то, что метод isPrototypeOf() играет роль, аналогичную оператору instanceof. Оператор instanceof позволяет проверить, к какому классу принадлежит объект, с учётом наследования.

class

Атрибут класса содержит строку с информацией о типе объекта. Ни в ECМAScript 5, ни в ECМAScript 3 нельзя устанавливать этот атрибут. Кроме того, существуют только непрямые способы его чтения. Метод toString() , унаследованный от Object.prototype, возвращает строку в следуюшем формате: [ object класс ]

Следовательно , для получения класса объекта можно вызвать метод toString() через этот объект, отсчитать восемь символов с начала и вернуть фрагмент строки. Главная сложность состоит в том, что многие объекты наследуют от других объектов более полезные варианты метода toString() , поэтому вызвать нужный вариант toString() можно только косвенно, с помощью метода Function.call() . В следующем листинге определена функция , возвращающая класс переданного ей объекта.

-24

extensible

Атрибут extensible (расширяемый) задает, можно ли добавить в объект новое свойство. Спецификация ECМAScript 5 определяет функции чтения и установки атрибута расширяемости объекта. Чтобы выяснить, является ли объект расширяемым, передайте его методу Object.preventExtensions() . Чтобы сделать объект нерасширяемым, передайте его объекту Object.preventExtensions() .

Метод Object.seal() , как и Object.preventExtensions() , делает объект нерасширяемым , но, кроме того, делает все собственные свойства объекта неконфигурируемыми. Следовательно , после его вызова в объект нельзя добавить новые свойства, а существующие свойства нельзя удалить или сконфигурировать. Чтобы выяснить, вызывался ли метод seal() , необходимо вызвать метод Object.isSealed() .

Метод Object.freeze() блокирует объекты еще жестче. Он не только делает объект нерасширяемым, а его свойства неконфигурируемыми, но и переключает все собственные свойства, не имеющие методов доступа, в режим "только чтение". Это называется замораживанием объекта. Чтобы выяснить, заморожен ли объект, нужно вызвать метод Object.isFrozen() .

Важно помнить, что результаты вызова методов Object.preventExtensions() , Object.seal() и Object.freeze() необратимые. Кроме того, эти методы влияют только на передаваемый им объект, а на прототипы объекта они никак не влияют. И наконец, все эти три метода возвращают переданный им объект в новом состоянии, благодаря чему вызовы этих методов можно делать вложенными.

-25

Если Вам нужна помощь или репетитор по физике, математике или информатике/программированию, Вы можете написать в группу Репетитор IT mentor в VK

Библиотека с книгами для физиков, математиков и программистов
Репетитор IT mentor в VK
Репетитор IT mentor в Instagram
Репетитор IT mentor в telegram