renovations

This commit is contained in:
Ilya Kantor 2015-01-14 21:17:25 +03:00
parent 166beb5e10
commit b4e31de966
33 changed files with 449 additions and 288 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View file

@ -13,7 +13,6 @@
Пример кода (кроме IE10-):
<div style="position:relative">
```js
//+ run
var animal = { eats: true };
@ -27,8 +26,6 @@ rabbit.__proto__ = animal;
alert(rabbit.jumps); // true
alert(rabbit.eats); // true
```
<div style="position:absolute;left:0;top:0;bottom:0;right:0"><img src="proto-animal-rabbit.svg"></div>
</div>
<ol>
<li>Первый `alert` здесь работает очевидным образом -- он выводит свойство `jumps` объекта `rabbit`.</li>
@ -37,18 +34,7 @@ alert(rabbit.eats); // true
Иллюстрация происходящего при чтении `rabbit.eats` (поиск идет снизу вверх):
```js
var animal = {
eats: true
};
var rabbit = {
jumps: true
};
```
<img src="1.png">
<img src="proto-animal-rabbit.svg">
**Объект, на который указывает ссылка `__proto__`, называется *"прототипом"*. В данном случае получилось, что `animal` является прототипом для `rabbit`.**
@ -72,6 +58,8 @@ alert(rabbit.eats); // false, свойство взято из rabbit
**Другими словами, прототип -- это "резервное хранилище свойств и методов" объекта, автоматически используемое при поиске.**
У объекта, который является `__proto__`, может быть свой `__proto__`, у того -- свой, и так далее. При этом свойства будут искаться по цепочке.
[smart header="Ссылка __proto__ в спецификации"]
Если вы будете читать спецификацию EcmaScript -- свойство `__proto__` обозначено в ней как `[[Prototype]]`.
@ -79,31 +67,7 @@ alert(rabbit.eats); // false, свойство взято из rabbit
[/smart]
## Цепочка прототипов
У объекта, который является `__proto__`, может быть свой `__proto__`, у того -- свой, и так далее.
Например, цепочка наследования из трех объектов `donkey -> winnie -> owl`:
```js
//+ run
var donkey = { /* ... */ };
var winnie = { /* ... */ };
var owl = { knowsAll: true };
donkey.__proto__ = winnie;
winnie.__proto__ = owl;
*!*
alert( donkey.knowsAll ); // true
*/!*
```
Картина происходящего:
<img src="donkey_winnie_owl.png">
## Перебор свойств без прототипа
## Метод hasOwnProperty
Обычный цикл `for..in` не делает различия между свойствами объекта и его прототипа.
@ -172,28 +136,28 @@ for (var key in rabbit) {
}
```
## Методы для работы с __proto__
В современных браузерах есть два дополнительных метода для работы с `__proto__`. Зачем они нужны, если есть `__proto__`? В общем-то, не очень нужны, но по историческим причинам тоже существуют.
<dl>
<dt>[Object.getPrototypeOf(obj)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/getPrototypeOf)</dt>
<dt>Чтение: [Object.getPrototypeOf(obj)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/getPrototypeOf)</dt>
<dd>Возвращает `obj.__proto__` (кроме IE8-)</dd>
<dt>[Object.setPrototypeOf(obj, proto)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/setPrototypeOf)</dt>
<dt>Запись: [Object.setPrototypeOf(obj, proto)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/setPrototypeOf)</dt>
<dd>Устанавливает `obj.__proto__ = proto` (кроме IE10-).</dd>
</dl>
Кроме того, есть ещё один вспомогательный метод:
<dt>[Object.create(proto)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create)</dt>
<dd>Создаёт пустой объект с `__proto__`, равным первому аргументу (кроме IE8-).</dd>
<dt>Создание объекта с прототипом: [Object.create(proto, descriptors)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create)</dt>
<dd>Создаёт пустой объект с `__proto__`, равным первому аргументу (кроме IE8-), второй необязательный аргумент может содержать [дескрипторы свойств](/descriptors-getters-setters).</dd>
</dl>
Метод `Object.create` -- несколько более мощный, чем здесь описано, у него есть необязательный второй аргумент, который позволяет также задать другие свойства объекта, но используется он редко и пока что нам не нужен. Мы рассмотрим его позже, в главе [](/descriptors-getters-setters).
## Итого
<ul>
<li>Объекты в JavaScript можно организовать в цепочку при помощи специального свойства `__proto__`.</li>
<li>В JavaScript есть встроенное "наследование" между объектами при помощи специального свойства `__proto__`.</li>
<li>При установке свойства `rabbit.__proto__ = animal` говорят, что объект `animal` будет "прототипом" `rabbit`.</li>
<li>При чтении свойства из объекта, если его в нём нет, оно ищется в `__proto__`. Прототип задействуется только при чтении свойства. Операции присвоения `obj.prop =` или удаления `delete obj.prop` совершаются всегда над самим объектом `obj`.</li>
</ul>
@ -205,12 +169,12 @@ for (var key in rabbit) {
<ul>
<li>[Object.getPrototypeOf(obj)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/getPrototypeOf) (кроме IE8-)</li>
<li>[Object.setPrototypeOf(obj, proto)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) (кроме IE10-)</li>
<li>[Object.create(proto)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create) (кроме IE8-)</li>
<li>[Object.create(proto, descriptors)](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create) (кроме IE8-)</li>
</ul>
Возможно, вас смущает недостаточная поддержка `__proto__` в старых IE. Но это временно. В последующих главах мы рассмотрим дополнительные методы работы с `__proto__`, включая те, которые работают везде.
Возможно, вас смущает недостаточная поддержка `__proto__` в старых IE. Но это не страшно. В последующих главах мы рассмотрим дополнительные методы работы с `__proto__`, включая те, которые работают везде.
Также мы рассмотрим, как свойство `__proto__` используется внутри самого языка JavaScript.
Также мы рассмотрим, как свойство `__proto__` используется внутри самого языка JavaScript и как организовать классы с его помощью.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 KiB

View file

@ -1,22 +1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="347px" height="163px" viewBox="0 0 347 163" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<svg width="140px" height="136px" viewBox="0 0 140 136" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
<title>proto-animal-rabbit</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="proto-animal-rabbit" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g sketch:type="MSLayerGroup">
<rect id="Rectangle-1" opacity="0.01" fill="#979797" sketch:type="MSShapeGroup" x="0" y="0" width="347" height="163"></rect>
<g id="Line-+-Line-+-Line" transform="translate(275.000000, 40.000000)" stroke="#D0011B" stroke-linecap="square" sketch:type="MSShapeGroup">
<path d="M0.5,101.514286 C0.5,101.514286 39.4570312,96.9360491 39.4570312,51.4857143 C39.4570312,6.03537946 0.5,1.45714286 0.5,1.45714286" id="Line"></path>
<path d="M0.5,1.45714286 L10.5,9.22857143" id="Line"></path>
<path d="M0.5,1.45714286 L13.5,1.45714286" id="Line"></path>
<g sketch:type="MSLayerGroup" transform="translate(0.000000, 1.000000)">
<g id="Rectangle-1-+-eats:-true-+-animal">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="120" height="30"></rect>
<text id="eats:-true" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="35">eats: true</tspan>
</text>
<text id="animal" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">animal</tspan>
</text>
</g>
<g id="Line-+-Line-+-Line-2" transform="translate(274.000000, 17.000000)" stroke="#D0011B" stroke-linecap="square" sketch:type="MSShapeGroup">
<path d="M0.5,2.45714286 L10.5,10.2285714" id="Line-3"></path>
<path d="M1.1875,19.9190476 C1.1875,19.9190476 15.7963867,19.1220178 15.7963867,11.2095238 C15.7963867,3.29702977 1.1875,2.5 1.1875,2.5" id="Line"></path>
<path d="M1.5,2.45714286 L12.5,0.5" id="Line-2"></path>
<g id="Rectangle-1-+-eats:-true-+-animal-2" transform="translate(0.000000, 88.000000)">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="120" height="30"></rect>
<text id="jumps:-true" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="35">jumps: true</tspan>
</text>
<text id="rabbit" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">rabbit</tspan>
</text>
</g>
<path d="M60.5,96.5 L60.5,55.5" id="Line" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-decoration-1" d="M60.5,55.5 C59.45,59.28 58.55,62.52 57.5,66.3 C59.6,66.3 61.4,66.3 63.5,66.3 C62.45,62.52 61.55,59.28 60.5,55.5 C60.5,55.5 60.5,55.5 60.5,55.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="70" y="77">__proto__</tspan>
</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before After
Before After

View file

@ -16,4 +16,19 @@ alert(obj2.name); // Петя (сработало)
Сработало, так как `User.prototype.constructor == User`.
Но если кто-то, к примеру, перезапишет `User.prototype` и забудет указать `constructor`, то такой фокус не пройдёт.
Но если кто-то, к примеру, перезапишет `User.prototype` и забудет указать `constructor`, то такой фокус не пройдёт, например:
```js
//+ run
function User(name) {
this.name = name;
}
*!*
User.prototype = {};
*/!*
var obj = new User('Вася');
var obj2 = new obj.constructor('Петя');
alert(obj2.name); // undefined
```

View file

@ -10,4 +10,4 @@
var obj2 = new obj.constructor();
```
В каком случае такой код будет работать, а в каком -- нет?
Приведите пример конструкторов для `obj`, при которых такой код будет работать верно -- и неверно.

View file

@ -2,7 +2,7 @@
До этого момента мы говорили о наследовании объектов, объявленных через `{...}`.
Но что, если объекты создаются функцией-конструктором через `new`? Как указать прототип в этом случае?
Но в реальных проектах объекты обычно создаются функцией-конструктором через `new`. Посмотрим, как указать прототип в этом случае.
[cut]
## Свойство F.prototype
@ -13,7 +13,7 @@
```js
//+ run
var animal = { eats: true }
var animal = { eats: true };
function Rabbit(name) {
this.name = name;
@ -57,9 +57,9 @@ alert( rabbit.eats ); // true
Установка `Rabbit.prototype = animal` буквально говорит интерпретатору следующее: *"При создании объекта через `new Rabbit` запиши ему `__proto__ = animal`".*
[smart header="Свойство `prototype` имеет смысл только у конструктора"]
Свойство `prototype` можно указать на любом объекте, но особый смысл оно имеет, лишь если назначено функции-конструктору.
Свойство с именем `prototype` можно указать на любом объекте, но особый смысл оно имеет, лишь если назначено функции-конструктору.
Само по себе оно вообще ничего не делает, его единственное назначение -- ставить `__proto__` новым объектам.
Само по себе, без вызова оператора `new`, оно вообще ничего не делает, его единственное назначение -- указывать `__proto__` для новых объектов.
[/smart]
@ -70,15 +70,81 @@ alert( rabbit.eats ); // true
Однако, при работе `new`, свойство `prototype` будет использовано лишь в том случае, если это объект. Примитивное значение, такое как число или строка, будет проигнорировано.
[/warn]
## Свойство constructor
У каждой функции по умолчанию уже есть свойство `prototype`.
Оно содержит объект такого вида:
```js
function Rabbit() { }
Rabbit.prototype = {
constructor: Rabbit
};
```
В коде выше я создал `Rabbit.prototype` вручную, но ровно такой же -- генерируется автоматически.
Проверим:
```js
//+ run
function Rabbit() { }
// в Rabbit.prototype есть одно свойство: constructor
alert(Object.getOwnPropertyNames(Rabbit.prototype)); // constructor
// оно равно Rabbit
alert(Rabbit.prototype.constructor == Rabbit); // true
```
Можно его использовать для создания объекта с тем же конструктором, что и данный:
```js
//+ run
function Rabbit(name) {
this.name = name;
alert(name);
}
var rabbit = new Rabbit("Кроль");
var rabbit2 = new rabbit.constructor("Крольчиха");
```
Эта возможность бывает полезна, когда, получив объект, мы не знаем в точности, какой у него был конструктор (например, сделан вне нашего кода), а нужно создать такой же.
[warn header="Свойство `constructor` легко потерять"]
JavaScript никак не использует свойство `constructor`. То есть, оно создаётся автоматически, а что с ним происходит дальше -- это уже наша забота. В стандарте прописано только его создание.
В частности, при перезаписи `Rabbit.prototype = { jumps: true }` свойства `constructor` больше не будет.
Сам интерпретатор JavaScript его в служебных целях не требует, поэтому в работе объектов ничего не "сломается". Но если мы хотим, чтобы возможность получить конструктор, всё же, была, то можно при перезаписи гарантировать наличие `constructor` вручную:
```js
Rabbit.prototype = {
jumps: true,
*!*
constructor: Rabbit
*/!*
};
```
Либо можно поступить аккуратно и добавить свойства к встроенному `prototype` без его замены:
```js
// сохранится встроенный constructor
Rabbit.prototype.jumps = true
```
[/warn]
## Эмуляция Object.create для IE8- [#inherit]
Как мы только что видели, с конструкторами всё просто, назначить прототип можно кросс-браузерно при помощи `F.prototype`.
Теперь вернёмся к созданию объектов без конструктора.
Теперь небольшое "лирическое отступление" в область совместимости.
Мы знаем, что в этом случае можно указывать прототип при помощи `__proto__`, но это не работает в IE10-. Также мы знаем, что есть метод `Object.create(proto)`, который создаёт пустой объект с данным прототипом, но он не работает в IE8-.
**Используя `prototype`, вызов `Object.create` можно легко эмулировать, так что он будет работать во всех браузерах, включая даже очень-очень старые.**
Прямые методы работы с прототипом осутствуют в старых IE, но один из них -- `Object.create(proto)` можно эмулировать, как раз при помощи `prototype`. И он будет работать везде, даже в самых устаревших браузерах.
Кросс-браузерный аналог -- назовём его `inherit`, состоит буквально из нескольких строк:
@ -91,7 +157,7 @@ function inherit(proto) {
}
```
Результат вызова `inherit(animal)` идентичен `Object.create(animal)`. Это будет новый пустой объект с прототипом `animal`.
Результат вызова `inherit(animal)` идентичен `Object.create(animal)`. Она создаёт новый пустой объект с прототипом `animal`.
Например:
@ -106,7 +172,7 @@ alert(rabbit.eats); // true
Посмотрите внимательно на функцию `inherit` и вы, наверняка, сами поймёте, как она работает...
Давайте, на всякий случай, пройдём её по шагам:
Если где-то неясности, то её построчное описание:
```js
function inherit(proto) {
@ -124,10 +190,7 @@ function inherit(proto) {
<li>Мы получили пустой объект с заданным прототипом, как и хотели. Возвратим его.</li>
</ol>
Эта функция широко используется в библиотеках и фреймворках.
Здесь и далее мы будем использовать `Object.create`, предполагая что для IE8- выполнен код:
Для унификации можно запустить такой код, и метод `Object.create` станет кросс-браузерным:
```js
if (!Object.create) Object.create = inherit; /* определение inherit - выше */
@ -135,12 +198,15 @@ if (!Object.create) Object.create = inherit; /* определение inherit -
В частности, аналогичным образом работает библиотека [es5-shim](https://github.com/es-shims/es5-shim), при подключении которой `Object.create` станет доступен для всех браузеров.
## Итого
Для произвольной функции -- назовём её `Constructor`, верно следующее:
<ul>
<li>Прототип новых объектов, создаваемых через `new`, можно задавать кросс-браузерно, при помощи свойства конструктора `prototype`.</li>
<li>При создании объекта через `new F`, в его `__proto__` записывается ссылка на объект `F.prototype`.</li>
<li>Современный метод `Object.create(proto)` можно эмулировать его при помощи `prototype`, если хочется, чтобы он работал в IE8-.</li>
<li>Прототип `__proto__` новых объектов, создаваемых через `new Constructor`, можно задавать при помощи свойства `Constructor.prototype`.</li>
<li>Значением `Constructor.prototype` по умолчанию является объект с единственным свойством `constructor`, содержащим ссылку на `Constructor`. Его можно использовать, чтобы из самого объекта получить функцию, которая его создала. Однако, JavaScript никак не поддерживает корректность этого свойства, поэтому программист может его изменить или удалить.</li>
<li>Современный метод `Object.create(proto)` можно эмулировать при помощи `prototype`, если хочется, чтобы он работал в IE8-.</li>
</ul>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

View file

@ -14,19 +14,26 @@ var obj = { };
alert( obj ); // "[object Object]" ?
```
В объекте, очевидно, ничего нет... Но кто же тогда генерирует строковое представление для `alert(obj)`?
Где код, который генерирует строковое представление для `alert(obj)`? Объект-то ведь пустой.
## Object.prototype
...Конечно же, это сделал метод `toString`, который находится во встроенном прототипе `Object.prototype`. Этот прототип ставится всем объектам `Object` при создании и содержит все встроенные методы и свойства для объектов.
...Конечно же, это сделал метод `toString`, который находится... Конечно, не в самом объекте (он пуст), а в его прототипе `obj.__proto__`, можно его даже вывести:
```js
//+ run
alert( {}.__proto__.toString ); // function toString
```
Откуда новый объект `obj` получает такой `__proto__`?
В деталях, работает это так:
<ol>
<li>Запись `obj = {}` является краткой формой `obj = new Object`, где `Object` -- встроенная функция-конструктор для объектов.</li>
<li>При выполнении `new Object`, создаваемому объекту ставится `__proto__` по `prototype` конструктора, то есть в данном случае `Object.prototype`.</li>
<li>При выполнении `new Object`, создаваемому объекту ставится `__proto__` по `prototype` конструктора, который в данном случае равен встроенному `Object.prototype`.</li>
<li>В дальнейшем при обращении к `obj.toString()` -- функция будет взята из `Object.prototype`.</li>
</ol>
<img src="5.png">
<img src="native-prototypes-object.svg">
Это можно легко проверить:
@ -39,26 +46,32 @@ alert(obj.toString == Object.prototype.toString); // true, да
// проверим, правда ли что __proto__ это Object.prototype?
alert(obj.__proto__ == Object.prototype); // true
// А есть ли __proto__ у Object.prototype?
alert(obj.__proto__.__proto__); // null, нет
```
## Встроенные "классы" в JavaScript
Точно такой же подход используется в массивах `Array`, функциях `Function` и других объектах. Встроенные методы для них находятся в `Array.prototype`, `Function.prototype` и т.п.
<img src="6.png">
<img src="native-prototypes-classes.svg">
Как видно из картинки, `Array.prototype` в свою очередь имеет прототипом `Object.prototype`, поэтому если метода нет у массива, то он ищется в объекте.
Например, когда мы создаём массив, `[1, 2, 3]`, то это альтернативный вариант синтаксиса `new Array`, так что у массивов есть стандартный прототип `Array.prototype`.
Например, при вызове `arr.hasOwnProperty(...)` для массива `arr` метод `hasOwnProperty` берётся из `Object.prototype`.
Но в нём есть методы лишь для массивов, а для общих методов всех объектов есть ссылка `Array.prototype.__proto__`, равная `Object.prototype`.
Получается иерархия наследования, которая всегда заканчивается на `Object.prototype`. Объект `Object.prototype` -- вершина иерархии, единственный, у которого `__proto__` равно `null`.
Аналогично, для функций.
Лишь для чисел (как и других примитивов) всё немного иначе, но об этом чуть далее.
Объект `Object.prototype` -- вершина иерархии, единственный, у которого `__proto__` равно `null`.
**Поэтому говорят, что "все объекты наследуют от `Object`", а если более точно, то от `Object.prototype`.**
**"Псевдоклассом" или, более коротко, "классом", называют функцию-конструктор вместе с её `prototype`.**
"Псевдоклассом" или, более коротко, "классом", называют функцию-конструктор вместе с её `prototype`. Такой способ объявления классов называют "прототипным стилем ООП".
[smart header="Переопределение методов в наследниках"]
**При наследовании часть методов переопределяется, например, у массива `Array` есть свой `toString`, который находится в `Array.prototype.toString`:**
При наследовании часть методов переопределяется, например, у массива `Array` есть свой `toString`, который выводит элементы массива через запятую:
```js
//+ run
@ -66,12 +79,14 @@ var arr = [1, 2, 3]
alert( arr ); // 1,2,3 <-- результат Array.prototype.toString
```
Для вывода объекта JavaScript ищет `toString` сначала в самом объекте `arr`, затем в `arr.__proto__`, который равен `Array.prototype`.
Как мы видели раньше, у `Object.prototype` есть свой `toString`, но так как в `Array.prototype` он ищется первым, то берётся именно вариант для массивов:
Конечно, если бы его там не было -- поиск пошёл бы выше в `Array.prototype.__proto__`, который по стандарту (см. диаграмму выше) равен `Object.prototype`, и тогда использовался бы стандартный метод для объектов.
[/smart]
<img src="native-prototypes-array-tostring.svg">
Ранее мы говорили о применении методов массивов к "псевдомассивам", например, можно использовать `[].join` для произвольных объектов, имеющих нумерованные свойства и `length`:
[smart header="Вызов методов через `apply` из прототипа"]
Ранее мы говорили о применении методов массивов к "псевдомассивам", например, можно использовать `[].join` для `arguments`:
```js
//+ run
@ -97,14 +112,16 @@ function showList() {
showList("Вася", "Паша", "Маша"); // Вася - Паша - Маша
```
Это лучше, потому что не создаётся лишний объект массива `[]`, хотя, с другой стороны -- так больше писать.
Это эффективнее, потому что не создаётся лишний объект массива `[]`, хотя, с другой стороны -- больше букв писать.
[/smart]
## Примитивы
Примитивы не являются объектами, но методы берут из соответствующих прототипов: `Number.prototype`, `Boolean.prototype`, `String.prototype`.
По стандарту, если обратиться к свойству примитива, то будет создан объект соответствующего типа, например `new String`, произведена операция со свойством или вызов метода по обычным правилам, с поиском в прототипе, а затем этот объект будет уничтожен.
По стандарту, если обратиться к свойству числа, строки или логического значения, то будет создан объект соответствующего типа, например `new String` для строки, `new Number` для чисел, `new Boolean` -- для логических выражений.
Далее будет произведена операция со свойством или вызов метода по обычным правилам, с поиском в прототипе, а затем этот объект будет уничтожен.
Именно так работает код ниже:
@ -114,17 +131,17 @@ var user = "Вася"; // создали строку (примитив)
*!*
alert( user.toUpperCase() ); // ВАСЯ
// создан временный объект new String
// был создан временный объект new String
// вызван метод
// new String уничтожен, результат возвращён
*/!*
```
**Принципиальное отличие от объектов -- в примитив нельзя записать свойство.**
Можно даже попробовать записать в этот временный объект свойство:
```js
//+ run
// а теперь попытаемся записать свойство в строку:
// попытаемся записать свойство в строку:
var user = "Вася";
user.age = 30;
@ -133,7 +150,7 @@ alert(user.age); // undefined
*/!*
```
Свойство `age` было записано во временный объект, который был тут же уничтожен.
Свойство `age` было записано во временный объект, который был тут же уничтожен, так что смысла в такой записи немного.
[warn header="Конструкторы `String/Number/Boolean` -- только для внутреннего использования"]
Технически, можно создавать объекты для примитивов и вручную, например `new Number`. Но в ряде случаев получится откровенно бредовое поведение. Например:
@ -163,7 +180,6 @@ if (zero) { // объект - true, так что alert выполнится
Значения `null` и `undefined` стоят особняком. Вышесказанное к ним не относится.
Для них нет соответствующих классов, в них нельзя записать свойство (будет ошибка), в общем, на конкурсе "самое примитивное значение" они точно разделили бы первое место.
[/warn]
@ -194,16 +210,16 @@ Object.prototype.each = function(f) {
}
// Попробуем! (внимание, пока что это работает неверно!)
var obj = { name: 'Вася', age: 25 };
var user = { name: 'Вася', age: 25 };
obj.each(function(prop, val) {
user.each(function(prop, val) {
alert(prop); // name -> age -> (!) each
});
```
Обратите внимание -- пример выше работает неправильно. Он выводит лишнее свойство `each`, т.к. цикл `for..in` перебирает свойства в прототипе. Встроенные методы при этом пропускаются, а наш метод -- вылез.
Обратите внимание -- пример выше работает не совсем корректно. Вместе со свойствами объекта `user` он выводит и наше свойство `each`. Технически, это правильно, так как цикл `for..in` перебирает свойства и в прототипе тоже, но не очень удобно.
В данном случае это легко поправить добавлением проверки `hasOwnProperty`:
Конечно, это легко поправить добавлением проверки `hasOwnProperty`:
```js
//+ run
@ -212,6 +228,7 @@ Object.prototype.each = function(f) {
for (var prop in this) {
*!*
// пропускать свойства из прототипа
if (!this.hasOwnProperty(prop)) continue;
*/!*
@ -230,28 +247,35 @@ obj.each(function(prop, val) {
});
```
Здесь это сработало, теперь код работает верно. Но мы же не хотим добавлять `hasOwnProperty` в цикл по любому объекту! Поэтому...
Здесь это сработало, теперь код работает верно. Но мы же не хотим добавлять `hasOwnProperty` в цикл по любому объекту! Поэтому либо не добавляйте свойства в `Object.prototype`, либо можно использовать [дескриптор свойства](/descriptors-getters-setters) и флаг `enumerable`.
Это, конечно, не будет работать в IE8-:
```js
//+ run
Object.prototype.each = function(f) {
[warn header="Не добавляйте свойства в `Object.prototype`"]
for (var prop in this) {
var value = this[prop];
f.call(value, prop, value);
}
Свойства, добавленные в `Object.prototype`, появятся во всех `for..in` циклах. Они в них будут лишними.
};
[/warn]
*!*
// поправить объявление свойства, установив флаг enumerable: false
Object.defineProperty(Object.prototype, 'each', { enumerable: false });
*/!*
// Теперь все будет в порядке
var obj = { name: 'Вася', age: 25 };
obj.each(function(prop, val) {
alert(prop); // name -> age
});
```
[smart header="Современный стандарт и `for..in`"]
Встроенные свойства и методы не перебираются в `for..in`, так как у них есть специальный внутренний флаг `[[Enumerable]]`, установленный в `false`.
Современные браузеры (включая IE с версии 9) позволяют устанавливать этот флаг для любых свойств, используя специальные вызовы, описанные в главе [](/descriptors-getters-setters). При таком добавлении предупреждение станет неактуальным, так как они тоже не будут видны в `for..in`.
[/smart]
**Многие объекты не участвуют в циклах `for..in`, например строки, функции... С ними уж точно нет такой проблемы, и в их прототипы, пожалуй, можно добавлять свои методы.**
Но здесь есть свои "за" и "против":
Есть несколько "за" и "против" модификации встроенных прототипов:
[compare]
+Методы в прототипе автоматически доступны везде, их вызов прост и красив.
@ -259,7 +283,7 @@ obj.each(function(prop, val) {
-Изменения встроенных прототипов влияют глобально, на все-все скрипты, делать их не очень хорошо с архитектурной точки зрения.
[/compare]
С другой стороны, есть одно исключение, когда изменения встроенных прототипов не только разрешены, но и приветствуются.
Как правило, минусы весомее, но есть одно исключение, когда изменения встроенных прототипов не только разрешены, но и приветствуются.
**Допустимо изменение прототипа встроенных объектов, которое добавляет поддержку метода из современных стандартов в те браузеры, где её пока нет.**

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="200px" height="233px" viewBox="0 0 200 233" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
<title>native-prototype-object</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="native-prototype-object" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g sketch:type="MSLayerGroup">
<g id="Rectangle-1-+-eats:-true-+-animal" transform="translate(0.000000, 69.000000)">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="200" height="60"></rect>
<text id="toString:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="41">toString: function</tspan>
<tspan x="11" y="57">другие методы объектов</tspan>
</text>
<text id="Object.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">Object.prototype</tspan>
</text>
</g>
<g id="Rectangle-1-+-eats:-true-+-animal-2" transform="translate(0.000000, 186.000000)">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="200" height="30"></rect>
<text id="obj" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">obj</tspan>
</text>
</g>
<path d="M60.5,194.5 L60.5,153.5" id="Line" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-decoration-1" d="M60.5,153.5 C59.45,157.28 58.55,160.52 57.5,164.3 C59.6,164.3 61.4,164.3 63.5,164.3 C62.45,160.52 61.55,157.28 60.5,153.5 C60.5,153.5 60.5,153.5 60.5,153.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="70" y="175">__proto__</tspan>
</text>
<path d="M60.5,62.5 L60.5,21.5" id="Line-2" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-2-decoration-1" d="M60.5,21.5 C59.45,25.28 58.55,28.52 57.5,32.3 C59.6,32.3 61.4,32.3 63.5,32.3 C62.45,28.52 61.55,25.28 60.5,21.5 C60.5,21.5 60.5,21.5 60.5,21.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__-2" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="70" y="43">__proto__</tspan>
</text>
<text id="null" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="46" y="10">null</tspan>
</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="201px" height="298px" viewBox="0 0 201 298" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
<title>native-prototypes-array-tostring</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="native-prototypes-array-tostring" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g sketch:type="MSLayerGroup" transform="translate(1.000000, 1.000000)">
<g id="Rectangle-1-+-eats:-true-+-animal" transform="translate(0.000000, 133.000000)">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="200" height="60"></rect>
<text id="toString:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="41">toString: function</tspan>
<tspan x="11" y="57">...</tspan>
</text>
<text id="Array.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">Array.prototype</tspan>
</text>
</g>
<g id="Rectangle-1-+-eats:-true-+-animal-3">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="200" height="60"></rect>
<text id="toString:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="41">toString: function</tspan>
<tspan x="11" y="57">...</tspan>
</text>
<text id="Object.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">Object.prototype</tspan>
</text>
</g>
<g id="Rectangle-1-+-eats:-true-+-animal-2" transform="translate(0.000000, 267.000000)" stroke="#979797" sketch:type="MSShapeGroup">
<rect id="Rectangle-1" x="0" y="0" width="200" height="30"></rect>
</g>
<path d="M60.5,258.5 L60.5,217.5" id="Line" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-decoration-1" d="M60.5,217.5 C59.45,221.28 58.55,224.52 57.5,228.3 C59.6,228.3 61.4,228.3 63.5,228.3 C62.45,224.52 61.55,221.28 60.5,217.5 C60.5,217.5 60.5,217.5 60.5,217.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="70" y="239">__proto__</tspan>
</text>
<path d="M60.5,126.5 L60.5,85.5" id="Line-2" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-2-decoration-1" d="M60.5,85.5 C59.45,89.28 58.55,92.52 57.5,96.3 C59.6,96.3 61.4,96.3 63.5,96.3 C62.45,92.52 61.55,89.28 60.5,85.5 C60.5,85.5 60.5,85.5 60.5,85.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__-2" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="70" y="107">__proto__</tspan>
</text>
<ellipse id="Oval-1" stroke="#D0011B" sketch:type="MSShapeGroup" cx="44" cy="170" rx="42" ry="16"></ellipse>
<text id="[1,-2,-3]" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="8" y="286">[1, 2, 3]</tspan>
</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="661px" height="399px" viewBox="0 0 661 399" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
<title>native-prototypes-classes</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="native-prototypes-classes" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g sketch:type="MSLayerGroup" transform="translate(1.000000, 0.000000)">
<g id="Rectangle-1-+-eats:-true-+-animal" transform="translate(226.000000, 69.000000)">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="200" height="60"></rect>
<text id="toString:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="41">toString: function</tspan>
<tspan x="11" y="57">другие методы объектов</tspan>
</text>
<text id="Object.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">Object.prototype</tspan>
</text>
</g>
<path d="M286.5,62.5 L286.5,21.5" id="Line-2" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-2-decoration-1" d="M286.5,21.5 C285.45,25.28 284.55,28.52 283.5,32.3 C285.6,32.3 287.4,32.3 289.5,32.3 C288.45,28.52 287.55,25.28 286.5,21.5 C286.5,21.5 286.5,21.5 286.5,21.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__-2" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="296" y="43">__proto__</tspan>
</text>
<path d="M286.5,195.5 L286.5,154.5" id="Line-3" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-3-decoration-1" d="M286.5,154.5 C285.45,158.28 284.55,161.52 283.5,165.3 C285.6,165.3 287.4,165.3 289.5,165.3 C288.45,161.52 287.55,158.28 286.5,154.5 C286.5,154.5 286.5,154.5 286.5,154.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="null" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="272" y="10">null</tspan>
</text>
<g id="Rectangle-1-+-eats:-true-+-animal-2" transform="translate(0.000000, 166.000000)">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="51" width="200" height="60"></rect>
<text id="slice:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="75">slice: function</tspan>
<tspan x="11" y="91">другие методы массивов</tspan>
</text>
<text id="__proto__-3" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="68" y="10">__proto__</tspan>
</text>
<text id="Array.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="44">Array.prototype</tspan>
</text>
</g>
<g id="Rectangle-1-+-eats:-true-+-animal-3" transform="translate(229.000000, 166.000000)">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="51" width="200" height="60"></rect>
<text id="__proto__" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="67" y="10">__proto__</tspan>
</text>
<text id="apply:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="75">apply: function</tspan>
<tspan x="11" y="91">другие методы функций</tspan>
</text>
<text id="Function.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="44">Function.prototype</tspan>
</text>
</g>
<g id="Rectangle-1-+-eats:-true-+-animal-4" transform="translate(460.000000, 166.000000)">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="51" width="200" height="60"></rect>
<text id="toFixed:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="75">toFixed: function</tspan>
<tspan x="11" y="91">другие методы чисел</tspan>
</text>
<text id="Number.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="44">Number.prototype</tspan>
</text>
<text id="__proto__-4" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="68" y="10">__proto__</tspan>
</text>
</g>
<path d="M129.5,187.5 L207.5,151.5" id="Line" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-decoration-1" d="M206.676548,151.880055 C202.80445,152.510737 199.485509,153.051323 195.613412,153.682005 C196.493434,155.58872 197.247739,157.223047 198.127761,159.129761 C201.119836,156.592364 203.684472,154.417452 206.676548,151.880055 C206.676548,151.880055 206.676548,151.880055 206.676548,151.880055 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<path d="M526.5,187.5 L449.5,151.5" id="Line" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-decoration-1" d="M449.5,151.5 C452.479529,154.052118 455.033412,156.239647 458.012941,158.791765 C458.902353,156.889412 459.664706,155.258824 460.554118,153.356471 C456.685176,152.706706 453.368941,152.149765 449.5,151.5 C449.5,151.5 449.5,151.5 449.5,151.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<rect id="Rectangle-5" stroke="#979797" sketch:type="MSShapeGroup" x="34" y="332" width="119" height="25"></rect>
<text id="[1,-2,-3]" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="58" y="348">[1, 2, 3]</tspan>
</text>
<rect id="Rectangle-6" stroke="#979797" sketch:type="MSShapeGroup" x="237" y="332" width="181" height="67"></rect>
<text id="function-f(args)-{" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="251" y="352">function f(args) {</tspan>
<tspan x="251" y="368"> ...</tspan>
<tspan x="251" y="384">}</tspan>
</text>
<rect id="Rectangle-7" stroke="#979797" sketch:type="MSShapeGroup" x="527" y="332" width="71" height="25"></rect>
<text id="5" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="560" y="349">5</tspan>
</text>
<path d="M286.5,325.5 L286.5,284.5" id="Line-4" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-4-decoration-1" d="M286.5,284.5 C285.45,288.28 284.55,291.52 283.5,295.3 C285.6,295.3 287.4,295.3 289.5,295.3 C288.45,291.52 287.55,288.28 286.5,284.5 C286.5,284.5 286.5,284.5 286.5,284.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__-5" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="296" y="306">__proto__</tspan>
</text>
<path d="M563.5,325.5 L563.5,284.5" id="Line-5" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-5-decoration-1" d="M563.5,284.5 C562.45,288.28 561.55,291.52 560.5,295.3 C562.6,295.3 564.4,295.3 566.5,295.3 C565.45,291.52 564.55,288.28 563.5,284.5 C563.5,284.5 563.5,284.5 563.5,284.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__-6" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="573" y="306">__proto__</tspan>
</text>
<path d="M93.5,325.5 L93.5,284.5" id="Line-6" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-6-decoration-1" d="M93.5,284.5 C92.45,288.28 91.55,291.52 90.5,295.3 C92.6,295.3 94.4,295.3 96.5,295.3 C95.45,291.52 94.55,288.28 93.5,284.5 C93.5,284.5 93.5,284.5 93.5,284.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__-7" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="103" y="306">__proto__</tspan>
</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.8 KiB

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="200px" height="233px" viewBox="0 0 200 233" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
<title>native-prototypes-object</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="native-prototypes-object" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g sketch:type="MSLayerGroup">
<g id="Rectangle-1-+-eats:-true-+-animal" transform="translate(0.000000, 69.000000)">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="200" height="60"></rect>
<text id="toString:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="11" y="41">toString: function</tspan>
<tspan x="11" y="57">другие методы объектов</tspan>
</text>
<text id="Object.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">Object.prototype</tspan>
</text>
</g>
<g id="Rectangle-1-+-eats:-true-+-animal-2" transform="translate(0.000000, 186.000000)">
<rect id="Rectangle-1" stroke="#979797" sketch:type="MSShapeGroup" x="0" y="17" width="200" height="30"></rect>
<text id="obj" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#000000">
<tspan x="0" y="10">obj</tspan>
</text>
</g>
<path d="M60.5,194.5 L60.5,153.5" id="Line" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-decoration-1" d="M60.5,153.5 C59.45,157.28 58.55,160.52 57.5,164.3 C59.6,164.3 61.4,164.3 63.5,164.3 C62.45,160.52 61.55,157.28 60.5,153.5 C60.5,153.5 60.5,153.5 60.5,153.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="70" y="175">__proto__</tspan>
</text>
<path d="M60.5,62.5 L60.5,21.5" id="Line-2" stroke="#4990E2" stroke-linecap="square" fill="#4990E2" sketch:type="MSShapeGroup"></path>
<path id="Line-2-decoration-1" d="M60.5,21.5 C59.45,25.28 58.55,28.52 57.5,32.3 C59.6,32.3 61.4,32.3 63.5,32.3 C62.45,28.52 61.55,25.28 60.5,21.5 C60.5,21.5 60.5,21.5 60.5,21.5 Z" stroke="#4990E2" stroke-linecap="square" fill="#4990E2"></path>
<text id="__proto__-2" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#4990E2">
<tspan x="70" y="43">__proto__</tspan>
</text>
<text id="null" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal" fill="#000000">
<tspan x="46" y="10">null</tspan>
</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -5,9 +5,9 @@
[cut]
## Обычный конструктор
Вспомним, как мы объявляли конструкторы ранее.
Вспомним, как мы объявляли классы ранее.
Например, этот код задаёт объект `Animal` без всяких прототипов:
Например, этот код задаёт класс `Animal` в функциональном стиле, без всяких прототипов:
```js
//+ run
@ -38,7 +38,8 @@ animal.stop(); // Зверь стоит
А теперь создадим аналогичный класс, используя прототипы, наподобие того, как сделаны классы `Object`, `Date` и остальные.
**Чтобы объявить свой класс, нужно:**
Чтобы объявить свой класс, нужно:
<ol>
<li>Объявить функцию-конструктор.</li>
<li>Записать методы и свойства, нужные всем объектам класса, в `prototype`.</li>
@ -51,6 +52,7 @@ animal.stop(); // Зверь стоит
// конструктор
function Animal(name) {
this.name = name;
this.speed = 0;
}
// методы в прототипе
@ -64,9 +66,6 @@ Animal.prototype.stop = function() {
alert(this.name + ' стоит');
};
// свойство speed со значением "по умолчанию"
Animal.prototype.speed = 0;
var animal = new Animal('Зверь');
alert(animal.speed); // 0, свойство взято из прототипа
@ -75,23 +74,21 @@ animal.run(5); // Зверь бежит, скорость 10
animal.stop(); // Зверь стоит
```
Здесь объекту `animal` принадлежит лишь свойство `name`, а остальное находится в прототипе.
В объекте `animal` будут хранится свойства конкретного экземпляра: `name` и `speed`, а общие методы -- в прототипе.
Обратим внимание, значение `speed` по умолчанию тоже перенесено в прототип, ведь оно во всех объектах (в начале) одинаково, но вызовы `animal.run()`, `animal.stop()` в примере используют вызов `this.speed += speed`, а любая запись в `this.свойство` работает с самим объектом.
То есть, начальное значение `speed` берётся из прототипа, а новое -- пишется уже в сам объект. И в дальнейшем используется.
<img src="7.png">
Совершенно такой же подход, как и для встроенных классов в JavaScript.
## Сравнение
Чем такое задание класса лучше и хуже предыдущего?
Чем такое задание класса лучше и хуже функционального стиля?
[compare]
+Прототип позволяет хранить общие свойства и методы в единственном экземпляре, таким образом экономится память и создание объекта происходит быстрее, чем если записывать их в каждый `this`.
-При объявлении класса через прототип, мы теряем возможность использовать локальные переменные и функции.
+Функциональный стиль записывает в каждый объект и свойства и методы, а прототипный -- только свойства. Поэтому прототипный стиль -- быстрее и экономнее по памяти.
-При создании методов через прототип, мы теряем возможность использовать локальные переменные как приватные свойства, у них больше нет общей области видимости с конструктором.
[/compare]
Таким образом, прототипный стиль -- быстрее и экономнее, но немного менее удобен.
К примеру, есть у нас приватное свойство `name` и метод `sayHi` в функциональном стиле ООП:
```js
@ -128,11 +125,4 @@ var animal = new Animal("Зверь");
animal.sayHi(); // Зверь
```
Ранее в каждый объект `Animal` записывалась своя функция `this.sayHi`, а теперь есть одна функция такого рода в прототипе. В сам объект мы пишем только то, что свойственно именно этому объекту.
## Задачи
Обычно свойства по умолчанию хранятся в прототипе. Но если свойство по умолчанию -- объект, то его в прототипе хранить нельзя.
Почему? Смотрите задачу ниже на эту тему.
Впрочем, недостаток этот -- довольно условный. Ведь при наследовании в функциональном стиле также пришлось бы писать `this._name`, чтобы потомок получил доступ к этому значению.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

View file

@ -1,141 +0,0 @@
# F.prototype по умолчанию, "constructor"
В стандарте JavaScript есть свойство `constructor`, которое, по идее, должно быть в каждом объекте и указывать, какой функцией он создан.
Однако на практике оно ведет себя не всегда адекватным образом. Мы рассмотрим, как оно работает и почему именно так, а также -- как сделать, чтобы оно работало правильно.
[cut]
## Свойство constructor [#constructor]
По замыслу, свойство `constructor` объекта должно содержать ссылку на функцию, создавшую объект. И в простейших случаях так оно и есть, вот например:
```js
//+ run
function Rabbit() { }
var rabbit = new Rabbit();
*!*
alert( rabbit.constructor == Rabbit ); // true
*/!*
```
Как видим, всё работает. Мы получили из объекта функцию, которая его создала.
Но всё не так просто. Расширим наш пример установкой `Rabbit.prototype`:
```js
//+ run
function Rabbit() { }
Rabbit.prototype = { jumps: true } ;
var rabbit = new Rabbit();
*!*
alert( rabbit.constructor == Rabbit ); // false (упс, потеряли конструктор!)
*/!*
```
...Сломалось! Чтобы детальнее понять происходящее -- посмотрим, откуда берется свойство `constructor` и что с ним произошло.
## Значение prototype по умолчанию
До этого мы записывали и меняли свойство `prototype`, но оказывается, оно существует и без нашего вмешательства.
**Свойство `prototype` есть у каждой функции, даже если его не ставить.**
**Оно создается автоматически вместе с самой функцией, и по умолчанию является пустым объектом с единственным свойством `constructor`, которое ссылается обратно на функцию.**
Вот такой вид имеет прототип по умолчанию:
```js
Rabbit.prototype = {
constructor: Rabbit
}
```
<img src="8.png">
Так что объект `rabbit` не хранит в себе свойство `constructor`, а берёт его из прототипа.
Сделаем прямую проверку этого:
```js
//+ run
function Rabbit() { }
var rabbit = new Rabbit();
*!*
alert( rabbit.hasOwnProperty('constructor') ); // false, в объекте нет!
alert( Rabbit.prototype.hasOwnProperty('constructor') ); // true, да, оно в прототипе
*/!*
```
## Потеря constructor
JavaScript не прилагает никаких усилий для проверки корректности или поддержания правильного значения `constructor`.
**При замене `Rabbit.prototype` на свой объект, свойство `constructor` из него обычно пропадает.**
Например:
```js
//+ run
function Rabbit() { }
Rabbit.prototype = {}; // (*)
var rabbit = new Rabbit();
alert( rabbit.constructor == Rabbit ); // false
alert( rabbit.constructor == Object ); // true
```
Ого! Теперь конструктором `rabbit` является не `Rabbit`, а `Object`. Но почему?
Вот иллюстрация, которая наглядно демонстрирует причину:
<img src="9.png">
То есть, свойство `constructor` бралось из `Rabbit.prototype`, но мы заменили его на пустой объект `Rabbit.prototype = {}`. В нём свойства `constructor` там уже нет, значит оно ищется дальше по цепочке прототипов, в `{}.__proto__`, то есть берётся из встроенного `Object.prototype`. А там оно равно `Object`.
Что делать?
**Если мы хотим, чтобы свойство `constructor` объекта всегда хранило функцию, которая его создала -- об этом нужно позаботиться самостоятельно.**
То есть, можно не заменять встроенный `Rabbit.prototype`, а расширить его. Или же, если обязательно нужно заменить, скажем при наследовании, то указать `constructor` явно:
```js
//+ run
function Animal() { }
Animal.prototype.run = function() { }
function Rabbit() { }
*!*
Rabbit.prototype = Object.create(Animal);
Rabbit.prototype.constructor = Rabbit;
*/!*
Rabbit.prototype.run = function() { }
var rabbit = new Rabbit();
*!*
alert( rabbit.constructor == Rabbit ); // true
*/!*
```
## Итого
При создании функции, её `prototype` -- это объект с единственным свойством `constructor`, которое указывает обратно на функцию.
**Забавен тот факт, что больше нигде в спецификации JavaScript свойство `constructor` не упоминается. Интерпретатор не использует его никак и нигде.**
В результате, в частности, `constructor` теряется при замене `prototype` на новый объект. И ничего страшного в этом нет.
Но если мы хотим использовать это свойство сами, чтобы при необходимости получать функцию-конструктор объекта, но при смене прототипа нужно самим смотреть, чтобы ненароком не перезаписать его.