You Don't Know JS: this & Object Prototypes / Вы не знаете JS: this и прототипы объектов: Chapter 3: Objects

← →
Английский оригинал Перевод на русский

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

# Вы не знаете JS: *this* и прототипы объектов
# Глава 3: Объекты

baniok 22.03.17 в 14:41

0

В первой и второй главе мы объяснили как `this` указывает на разные объекты, в зависимости от места вызова функции. Но что представляют собой объекты на самом деле и почему нам нужно указывать на них? Мы подробно рассмотрим объекты в этой главе.

baniok 23.03.17 в 09:08

0

## Синтаксис

baniok 23.03.17 в 09:09

0

Объекты создаются двумя способами: декларативно (литерально) и с помощью конструктора.

baniok 23.03.17 в 09:14

0

Литеральный синтаксис для объекта выглядит так:

baniok 23.03.17 в 09:19

0

```js
var myObj = {
key: value
// ...
};
```

baniok 23.03.17 в 09:20

0

Конструкторная форма выглядит так:

baniok 23.03.17 в 09:20

0

```js
var myObj = new Object();
myObj.key = value;
```

baniok 23.03.17 в 09:20

0

Конструкторная и литеральная формы в результате дают одинаковые объекты. Единственное отличие в том, что в литеральной форме вы можете добавлять сразу несколько пар ключ-значение, в то время как с конструктором вам нужно добавлять свойства по одному.

baniok 23.03.17 в 09:28

0

**Примечание:** Конструкторная форма для создания объектов, показанная выше, используется крайне редко. Почти всегда вы предпочтёте использовать литеральную форму. Это справедливо и для большинства встроенных объектов (смотрите ниже).

baniok 23.03.17 в 09:31

0

## Тип

baniok 23.03.17 в 09:32

0

Объекты — это основные элементы из которых построена большая часть JS. В JS есть шесть основных типов (в спецификации называются «языковые типы»):

baniok 23.03.17 в 09:46

0

* `string`
* `number`
* `boolean`
* `null`
* `undefined`
* `object`

baniok 23.03.17 в 09:46

0

Обратите внимание, что *простые примитивы* (`string`, `number`, `boolean`, `null`, and `undefined`) сами по себе **не являются** объектами. `null` иногда упоминается как объект, но это заблуждение произошло из ошибки в языке, которая приводит к тому, что `typeof null` ошибочно возвращает `"object"`. По факту, `null` это самостоятельный примитивный тип.

baniok 23.03.17 в 09:50

0

**Есть распространённое заблуждение, что "в ДжаваСкрипте всё является объектом". Это совсем не так.**

baniok 23.03.17 в 10:16

0

Однако, *есть* несколько специальных подтипов, которые мы можем называть *сложными примитивами*.

baniok 23.03.17 в 10:18

0

`function` — это подтип объекта (технически, «вызываемый объект»). Говорят, что функции в JS это объекты «первого класса», поскольку в основном они являются обычными объектами и с ними можно работать как с любым другим объектом.

baniok 23.03.17 в 10:35

0

"Функция" - это подтип объекта (формально, "вызываемый объект"). Функции в JS - это, как говорится "первый класс", так как они являются по сути просто обычными объектами (с прикрученной семантикой вызова), и поэтому с ними можно обращаться как с любыми другими обыкновенными объектами.

p_zombie 23.05.17 в 05:35

0

Массивы это тоже форма объекта с расширенным поведением. Содержимое массивов организовано более структурированно чем у обычных объектов.

baniok 23.03.17 в 10:37

0

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

p_zombie 23.05.17 в 05:42

0

### Встроенные Объекты

baniok 23.03.17 в 10:38

0

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

baniok 23.03.17 в 10:46

0

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

p_zombie 23.05.17 в 06:13

0

* `String`
* `Number`
* `Boolean`
* `Object`
* `Function`
* `Array`
* `Date`
* `RegExp`
* `Error`

baniok 23.03.17 в 10:46

0

Эти подтипы выглядят как настоящие типы или даже классы, если вы полагаетесь на сходство с другими языками вроде класса `String` в Джаве.

baniok 23.03.17 в 10:48

0

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

p_zombie 23.05.17 в 07:15

0

Но в JS это на самом деле встроенные функции. Каждая из этих встроенных функций может быть использована как конструктор (ага, вызов функции с оператором `new` -- смотрите Главу 2), а результатом будет новый *сконструированный* объект указанного подтипа. Например:

baniok 23.03.17 в 11:33

0

Но в JS, они на самом деле просто встроенные функции. Каждая из этих встроенных функций может использоваться как конструктор (то есть, вызов функции с оператором 'new' -- смотрите Главу 2), в результате получится новый *построенный* объект соответствующего подтипа. Например:

p_zombie 23.05.17 в 07:28

0

```js
var strPrimitive = "I am a string";
typeof strPrimitive; // "string"
strPrimitive instanceof String; // false
var strObject = new String( "I am a string" );
typeof strObject; // "object"
strObject instanceof String; // true
// проверим подтип объекта
Object.prototype.toString.call( strObject ); // [object String]
```

baniok 23.03.17 в 11:35

0

В следующей главе мы подробно рассмотрим как именно работает `Object.prototype.toString...`. Вкратце: мы можем проверить внутренний подтип, заимствовав базовый стандартный метод `toString()`. Как видите, он показывает, что этот `strObject` — на самом деле объект, созданный конструктором `String`.

baniok 23.03.17 в 11:52

0

Примитивное значение «I am a string» это не объект, а литерал примитива и его значение иммутабельно. Чтобы выполнять над ним операции вроде проверки длинны, доступа к содержимому символов и т. д. требуется объект `String`.

baniok 23.03.17 в 13:54

0

К счастью, при необходимости язык автоматически приводит примитив `"string"` к объекту `String`, а значит вам почти никогда не придется дополнительно создавать форму Объекта. Большая часть сообщества JS **настоятельно рекомендует** по возможности использовать литеральную форму вместо конструкторной формы.

baniok 23.03.17 в 13:58

0

Рассмотрим:

baniok 23.03.17 в 13:59

0

```js
var strPrimitive = "I am a string";
console.log( strPrimitive.length ); // 13
console.log( strPrimitive.charAt( 3 ) ); // "m"
```

baniok 23.03.17 в 13:59

0

В обоих случаях мы вызываем свойство или метод строчного примитива и движок автоматически преоборазует его к объекту `String`, так что свойство/метод работают.

baniok 23.03.17 в 14:03

0

Такое же преобразование происходит между численным литеральным примитивом `40` и обёрткой объекта `new Number(42)` при использовании методов вроде `42.359.toFixed(2)`. То же самое и с объектом `Boolean` из примитива `"boolean"`.

baniok 23.03.17 в 14:18

0

`null` и `undefined` не имеют формы Объекта, только значения их примитивов. Для примера, значения `Date` могут быть созданы *только* с помощью их конструкторной формы объекта, так как у них нет соответствующей литеральной формы.

baniok 23.03.17 в 14:21

0

`Object`, `Array`, `Function`, и `RegExp` (регулярные выражения) — это объекты, не зависимо от того используется литеральная или конструкторная форма. Конструкторная форма в некоторых случаях предлагает больше опций для создания, чем литеральная. Поскольку объекты создаются в любом случае, простая литеральная форма почти универасльна. **Используйте конструкторную форму только если вам нужны дополнительные опции.**

baniok 24.03.17 в 08:47

0

Объекты `Error` редко создаются в коде в явном виде. Обычно они создаются автоматически когда появляются исключения. Они могут быть созданы с помощью конструкторной формы `new Error(..)`, но зачастую в этом нет необходимости.

baniok 24.03.17 в 08:50

0

## Содержимое

baniok 24.03.17 в 08:54

0

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

baniok 27.03.17 в 09:15

0

Важно отметить, что когда мы говорим о «содержимом» и подразумеваем, что эти значения хранятся прямо внутри объекта, то это лишь представление. Движок хранит значения в зависимости от его реализации и может запросто не хранить их *внутри* какого-нибудь контейнера объекта. Что *действительно* хранится в контейнере, так это названия свойств, которые работают как указатели (технически, *ссылаются*) туда, где хранятся значения.

baniok 27.03.17 в 09:26

0

Рассмотрим:

baniok 27.03.17 в 09:36

0

```js
var myObject = {
a: 2
};
myObject.a; // 2
myObject["a"]; // 2
```

baniok 27.03.17 в 09:37

0

Чтобы получить значение по *адресу* `a` в `myObject` мы должны использовать либо оператор `.` либо оператор `[]`. Синтаксис `.a` обычно описывают как доступ к «свойству», а синтаксис `["a"]` называют доступ по «ключу». В реальности они оба обращаются к одному *адресу* и выведут одно и то же значение `2`, так что эти термины взаимозаменяемы. Начиная отсюда мы будем использовать наиболее общий термин «доступ к свойству».

baniok 27.03.17 в 09:47

0

Основное различие между двумя синтаксисами в том, что оператор `.` требует, чтобы после него шло название свойства, совместимое с `Идентификатором`, в то время как синтаксис `[".."]` может принять в качестве имени свойства любую строку, совместимую с UTF-8/unicode.

baniok 27.03.17 в 09:52

0

Также, поскольку синтаксис `[".."]` использует **значение** строки для указания адреса, можно программно сгенерировать значение этой строки, например, так:

baniok 27.03.17 в 10:29

0

```js
var wantA = true;
var myObject = {
a: 2
};
var idx;
if (wantA) {
idx = "a";
}
// later
console.log( myObject[idx] ); // 2
```

baniok 27.03.17 в 10:27

0

В объектах названия свойств **всегда** являются строкой. Если вы используете в качестве свойств любые другие значения кроме `string` (примитив), они будут сконвертированы в строку. Это относится и к числам, которые обычно используют как индексы массивов. Так что будте осторожны и не путайте использование чисел в объектах и массивах.

baniok 27.03.17 в 10:36

0

```js
var myObject = { };
myObject[true] = "foo";
myObject[3] = "bar";
myObject[myObject] = "baz";
myObject["true"]; // "foo"
myObject["3"]; // "bar"
myObject["[object Object]"]; // "baz"
```

baniok 27.03.17 в 10:44

0

### Вычисляемые имена свойств

baniok 27.03.17 в 10:45

0

Описанный выше синтаксис доступа к свойствам вида `myObject[..]` полезен когда необходимо использовать результат выражения *как* имя ключа, вроде `myObject[prefix + name]`. Но это не сильно помогает при объявлении объектов через объектно-литеральный синтаксис.

baniok 27.03.17 в 10:48

0

ES6 добавляет *вычисляемые имена свойств*, где можно указать выражение, обрамленное `[ ]`, в качестве пары ключ-значение при литеральном объявлении объекта:

baniok 27.03.17 в 10:53

0

```js
var prefix = "foo";
var myObject = {
[prefix + "bar"]: "hello",
[prefix + "baz"]: "world"
};
myObject["foobar"]; // hello
myObject["foobaz"]; // world
```

baniok 27.03.17 в 11:17

0

Наиболее распространённым, вероятно, будет использование *вычислимых имен свойств* для `Symbol` в ES6, которые не будут подробно рассмотриваться в этой книге. Вкратце, это новый примитивный тип данных, значение которого непрозрачно и неопределимо (технически, это значение — `string`). Вы будете сильно озадачены, работая с *действительным значением* `Symbol` (которое теоретически может быть разным в разных движках JS), поэтому вы будете использовать имя Символа, вроде `Symbol.Something` (просто выдуманное имя!):

baniok 27.03.17 в 11:22

0
Минутку...

Следующая страница →