renovations

This commit is contained in:
Ilya Kantor 2015-01-14 10:23:45 +03:00
parent c7d4c7e3ff
commit e1948130f6
170 changed files with 1496 additions and 1161 deletions

View file

@ -0,0 +1,103 @@
# Секретное свойство [[Class]]
Для встроенных объектов есть одна "секретная" возможность узнать их тип, которая связана с методом `toString`.
Во всех встроенных объектах есть специальное свойство `[[Class]]`, в котором хранится информация о его типе или конструкторе.
Оно взято в квадратные скобки, так как это свойство -- внутреннее. Явно получить его нельзя, но можно прочитать его "в обход", воспользовавшись методом `toString` из `Object`.
[cut]
## Получение [[Class]]
Вернёмся к примеру, который видели раньше:
```js
//+ run
var obj = {};
alert( obj ); // [object Object]
```
**В выводе стандартного `toString` для объектов внутри `[object ...]` указано как раз значение `[[Class]]`.**
Для обычного объекта это как раз и есть `"Object"`, но если бы такой `toString` запустить для даты, то будет `[object Date]`, для массивов -- `[object Array]` и т.п.
К сожалению или к счастью, но большинство встроенных объектов в JavaScript имеют свой собственный метод `toString`: для массивов он выводит элементы через запятую, для дат -- строчное представление и так далее.
То есть, просто вызов `[1,2,3].toString()` вернёт нам `1,2,3` и никакой информации про `[[Class]]`.
Поэтому для получения `[[Class]]` мы одолжим функцию `toString` у стандартного объекта и запустим её в контексте тех значений, для которых нужно получить тип. В этом нам поможет метод `call`:
```js
//+ run
var toClass = {}.toString; // (1)
var arr = [1,2];
alert( toClass.call(arr) ); // (2) [object Array]
var date = new Date;
alert( toClass.call(date) ); // [object Date]
var type = toClass.call(date).slice(8, -1); // (3)
alert(type); // Date
```
Разберем происходящее более подробно.
<ol>
<li>Можно переписать эту строку в две:
```js
var obj = {};
var toClass = obj.toString;
```
Иначе говоря, мы создаём пустой объект `{}` и копируем ссылку на его метод `toString` в переменную `toClass`.
**Для получения `[[Class]]` нужна именно внутренняя реализация `toString` стандартного объекта `Object`, другая не подойдёт.**</li>
<li>Вызываем скопированный метод в контексте нужного объекта `obj`.
Мы могли бы поступить проще -- одолжить метод под другим названием:
```js
//+ run
var arr = [1,2];
arr.toClass = {}.toString;
alert( arr.toClass() ); // [object Array]
```
...Но зачем копировать лишнее свойство в объект? Синтаксис `toClass.call(arr)` делает то же самое, поэтому используем его.
</li>
<li>Всё, класс получен. При желании можно убрать обёртку `[object ...]`, взяв подстроку вызовом `slice(8,-1)`.</li>
</ol>
Метод также можно использовать с примитивами:
```js
//+ run
alert( {}.toString.call(123) ); // [object Number]
alert( {}.toString.call("строка") ); // [object String]
```
[warn header="Вызов `{}.toString` в консоли может выдать ошибку"]
При тестировании кода в консоли вы можете обнаружить, что если ввести в командную строку `{}.toString.call(...)` -- будет ошибка. С другой стороны, вызов `alert( {}.toString... )` -- работает.
Эта ошибка возникает потому, что фигурные скобки `{ }` в основном потоке кода интерпретируются как блок. Интерпретатор читает `{}.toString.call(...)` так:
```js
{ } // пустой блок кода
.toString.call(...) // а что это за точка в начале? не понимаю, ошибка!
```
Фигурные скобки считаются объектом, только если они находятся в контексте выражения. В частности, оборачивание в скобки `( {}.toString... )` тоже сработает нормально.
[/warn]
## Итого
<ul>
<li>Свойство `[[Class]]` позволяет получить тип для встроенных объектов. Далее мы будем рассматривать создание своих объектов через функцию-конструктор, с ними `[[Class]]` не работает.</li>
<li>Для доступа к `[[Class]]` используется `{}.toString.call(obj).slice(8, -1)`.</li>
</ul>
Обычно в JavaScript используется "утиная" типизация. Свойство `[[Class]]` -- самое надёжное средство проверки типа встроенных объектов, но обычно утиной типизации вполне хватает.