You Don't Know JS: this & Object Prototypes / Вы не знаете JS: this и прототипы объектов: Chapter 3: Objects
| Английский оригинал | Перевод на русский | |
|---|---|---|
Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
# Вы не знаете JS: *this* и прототипы объектов baniok 22.03.17 в 14:41 |
|
|
В первой и второй главе мы объяснили как `this` указывает на разные объекты, в зависимости от места вызова функции. Но что представляют собой объекты на самом деле и почему нам нужно указывать на них? Мы подробно рассмотрим объекты в этой главе. baniok 23.03.17 в 09:08 |
||
|
## Синтаксис baniok 23.03.17 в 09:09 |
||
|
Объекты создаются двумя способами: декларативно (литерально) и с помощью конструктора. baniok 23.03.17 в 09:14 |
||
|
Литеральный синтаксис для объекта выглядит так: baniok 23.03.17 в 09:19 |
||
|
```js baniok 23.03.17 в 09:20 |
||
|
Конструкторная форма выглядит так: baniok 23.03.17 в 09:20 |
||
|
```js baniok 23.03.17 в 09:20 |
||
|
Конструкторная и литеральная формы в результате дают одинаковые объекты. Единственное отличие в том, что в литеральной форме вы можете добавлять сразу несколько пар ключ-значение, в то время как с конструктором вам нужно добавлять свойства по одному. baniok 23.03.17 в 09:28 |
||
|
**Примечание:** Конструкторная форма для создания объектов, показанная выше, используется крайне редко. Почти всегда вы предпочтёте использовать литеральную форму. Это справедливо и для большинства встроенных объектов (смотрите ниже). baniok 23.03.17 в 09:31 |
||
|
## Тип baniok 23.03.17 в 09:32 |
||
|
Объекты — это основные элементы из которых построена большая часть JS. В JS есть шесть основных типов (в спецификации называются «языковые типы»): baniok 23.03.17 в 09:46 |
||
|
* `string` baniok 23.03.17 в 09:46 |
||
|
Обратите внимание, что *простые примитивы* (`string`, `number`, `boolean`, `null`, and `undefined`) сами по себе **не являются** объектами. `null` иногда упоминается как объект, но это заблуждение произошло из ошибки в языке, которая приводит к тому, что `typeof null` ошибочно возвращает `"object"`. По факту, `null` это самостоятельный примитивный тип. baniok 23.03.17 в 09:50 |
||
|
**Есть распространённое заблуждение, что "в ДжаваСкрипте всё является объектом". Это совсем не так.** baniok 23.03.17 в 10:16 |
||
|
Однако, *есть* несколько специальных подтипов, которые мы можем называть *сложными примитивами*. baniok 23.03.17 в 10:18 |
||
|
`function` — это подтип объекта (технически, «вызываемый объект»). Говорят, что функции в JS это объекты «первого класса», поскольку в основном они являются обычными объектами и с ними можно работать как с любым другим объектом. baniok 23.03.17 в 10:35 "Функция" - это подтип объекта (формально, "вызываемый объект"). Функции в JS - это, как говорится "первый класс", так как они являются по сути просто обычными объектами (с прикрученной семантикой вызова), и поэтому с ними можно обращаться как с любыми другими обыкновенными объектами. p_zombie 23.05.17 в 05:35 |
||
|
Массивы это тоже форма объекта с расширенным поведением. Содержимое массивов организовано более структурированно чем у обычных объектов. baniok 23.03.17 в 10:37 Массивы это также форма объектов, но с расширенным поведением. Организация содержимого в массивах чуть более логичное чем у основных объектов. p_zombie 23.05.17 в 05:42 |
||
|
### Встроенные Объекты baniok 23.03.17 в 10:38 |
||
|
Существует несколько других подтипов объектов, обычно называемых встроенными объектами. Некоторые их названия подразумевают, что они непосредственно относятся к соответствующим простым примитивами. На самом деле их отношения гораздо сложнее. Скоро мы их рассмотрим подробнее. baniok 23.03.17 в 10:46 Есть несколько иных объектов, обычно называемых встроенными объектами. p_zombie 23.05.17 в 06:13 |
||
|
* `String` baniok 23.03.17 в 10:46 |
||
|
Эти подтипы выглядят как настоящие типы или даже классы, если вы полагаетесь на сходство с другими языками вроде класса `String` в Джаве. baniok 23.03.17 в 10:48 Эти встроенные объекты имеют видимость типов, даже классов, если вы полагаетесь на схожесть с другими языками, например класс 'String' в Java. p_zombie 23.05.17 в 07:15 |
||
|
Но в JS это на самом деле встроенные функции. Каждая из этих встроенных функций может быть использована как конструктор (ага, вызов функции с оператором `new` -- смотрите Главу 2), а результатом будет новый *сконструированный* объект указанного подтипа. Например: baniok 23.03.17 в 11:33 Но в JS, они на самом деле просто встроенные функции. Каждая из этих встроенных функций может использоваться как конструктор (то есть, вызов функции с оператором 'new' -- смотрите Главу 2), в результате получится новый *построенный* объект соответствующего подтипа. Например: p_zombie 23.05.17 в 07:28 |
||
|
```js baniok 23.03.17 в 11:35 |
||
|
В следующей главе мы подробно рассмотрим как именно работает `Object.prototype.toString...`. Вкратце: мы можем проверить внутренний подтип, заимствовав базовый стандартный метод `toString()`. Как видите, он показывает, что этот `strObject` — на самом деле объект, созданный конструктором `String`. baniok 23.03.17 в 11:52 |
||
|
Примитивное значение «I am a string» это не объект, а литерал примитива и его значение иммутабельно. Чтобы выполнять над ним операции вроде проверки длинны, доступа к содержимому символов и т. д. требуется объект `String`. baniok 23.03.17 в 13:54 |
||
|
К счастью, при необходимости язык автоматически приводит примитив `"string"` к объекту `String`, а значит вам почти никогда не придется дополнительно создавать форму Объекта. Большая часть сообщества JS **настоятельно рекомендует** по возможности использовать литеральную форму вместо конструкторной формы. baniok 23.03.17 в 13:58 |
||
|
Рассмотрим: baniok 23.03.17 в 13:59 |
||
|
```js baniok 23.03.17 в 13:59 |
||
|
В обоих случаях мы вызываем свойство или метод строчного примитива и движок автоматически преоборазует его к объекту `String`, так что свойство/метод работают. baniok 23.03.17 в 14:03 |
||
|
Такое же преобразование происходит между численным литеральным примитивом `40` и обёрткой объекта `new Number(42)` при использовании методов вроде `42.359.toFixed(2)`. То же самое и с объектом `Boolean` из примитива `"boolean"`. baniok 23.03.17 в 14:18 |
||
|
`null` и `undefined` не имеют формы Объекта, только значения их примитивов. Для примера, значения `Date` могут быть созданы *только* с помощью их конструкторной формы объекта, так как у них нет соответствующей литеральной формы. baniok 23.03.17 в 14:21 |
||
|
`Object`, `Array`, `Function`, и `RegExp` (регулярные выражения) — это объекты, не зависимо от того используется литеральная или конструкторная форма. Конструкторная форма в некоторых случаях предлагает больше опций для создания, чем литеральная. Поскольку объекты создаются в любом случае, простая литеральная форма почти универасльна. **Используйте конструкторную форму только если вам нужны дополнительные опции.** baniok 24.03.17 в 08:47 |
||
|
Объекты `Error` редко создаются в коде в явном виде. Обычно они создаются автоматически когда появляются исключения. Они могут быть созданы с помощью конструкторной формы `new Error(..)`, но зачастую в этом нет необходимости. baniok 24.03.17 в 08:50 |
||
|
## Содержимое baniok 24.03.17 в 08:54 |
||
|
Как мы упоминали ранее, содержимое объекта состоит из значений (любого типа), которые хранятся в специально названных *местах*, которые мы называем свойствами. baniok 27.03.17 в 09:15 |
||
|
Важно отметить, что когда мы говорим о «содержимом» и подразумеваем, что эти значения хранятся прямо внутри объекта, то это лишь представление. Движок хранит значения в зависимости от его реализации и может запросто не хранить их *внутри* какого-нибудь контейнера объекта. Что *действительно* хранится в контейнере, так это названия свойств, которые работают как указатели (технически, *ссылаются*) туда, где хранятся значения. baniok 27.03.17 в 09:26 |
||
|
Рассмотрим: baniok 27.03.17 в 09:36 |
||
|
```js baniok 27.03.17 в 09:37 |
||
|
Чтобы получить значение по *адресу* `a` в `myObject` мы должны использовать либо оператор `.` либо оператор `[]`. Синтаксис `.a` обычно описывают как доступ к «свойству», а синтаксис `["a"]` называют доступ по «ключу». В реальности они оба обращаются к одному *адресу* и выведут одно и то же значение `2`, так что эти термины взаимозаменяемы. Начиная отсюда мы будем использовать наиболее общий термин «доступ к свойству». baniok 27.03.17 в 09:47 |
||
|
Основное различие между двумя синтаксисами в том, что оператор `.` требует, чтобы после него шло название свойства, совместимое с `Идентификатором`, в то время как синтаксис `[".."]` может принять в качестве имени свойства любую строку, совместимую с UTF-8/unicode. baniok 27.03.17 в 09:52 |
||
|
Также, поскольку синтаксис `[".."]` использует **значение** строки для указания адреса, можно программно сгенерировать значение этой строки, например, так: baniok 27.03.17 в 10:29 |
||
|
```js baniok 27.03.17 в 10:27 |
||
|
В объектах названия свойств **всегда** являются строкой. Если вы используете в качестве свойств любые другие значения кроме `string` (примитив), они будут сконвертированы в строку. Это относится и к числам, которые обычно используют как индексы массивов. Так что будте осторожны и не путайте использование чисел в объектах и массивах. baniok 27.03.17 в 10:36 |
||
|
```js baniok 27.03.17 в 10:44 |
||
|
### Вычисляемые имена свойств baniok 27.03.17 в 10:45 |
||
|
Описанный выше синтаксис доступа к свойствам вида `myObject[..]` полезен когда необходимо использовать результат выражения *как* имя ключа, вроде `myObject[prefix + name]`. Но это не сильно помогает при объявлении объектов через объектно-литеральный синтаксис. baniok 27.03.17 в 10:48 |
||
|
ES6 добавляет *вычисляемые имена свойств*, где можно указать выражение, обрамленное `[ ]`, в качестве пары ключ-значение при литеральном объявлении объекта: baniok 27.03.17 в 10:53 |
||
|
```js baniok 27.03.17 в 11:17 |
||
|
Наиболее распространённым, вероятно, будет использование *вычислимых имен свойств* для `Symbol` в ES6, которые не будут подробно рассмотриваться в этой книге. Вкратце, это новый примитивный тип данных, значение которого непрозрачно и неопределимо (технически, это значение — `string`). Вы будете сильно озадачены, работая с *действительным значением* `Symbol` (которое теоретически может быть разным в разных движках JS), поэтому вы будете использовать имя Символа, вроде `Symbol.Something` (просто выдуманное имя!): baniok 27.03.17 в 11:22 |
