5.8 KiB
Секретное свойство Class
Для встроенных объектов есть одна "секретная" возможность узнать их тип, которая связана с методом toString
.
Во всех встроенных объектах есть специальное свойство [[Class]]
, в котором хранится информация о его типе или конструкторе.
Оно взято в квадратные скобки, так как это свойство -- внутреннее. Явно получить его нельзя, но можно прочитать его "в обход", воспользовавшись методом toString
из Object
.
[cut]
Получение Class
Вернёмся к примеру, который видели раньше:
//+ 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
:
//+ 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
Разберем происходящее более подробно.
- Можно переписать эту строку в две:
var obj = {}; var toClass = obj.toString;
Иначе говоря, мы создаём пустой объект
{}
и копируем ссылку на его методtoString
в переменнуюtoClass
.Для получения
[[Class]]
нужна именно внутренняя реализацияtoString
стандартного объектаObject
, другая не подойдёт. - Вызываем скопированный метод в контексте нужного объекта `obj`.
Мы могли бы поступить проще -- одолжить метод под другим названием:
//+ run var arr = [1,2]; arr.toClass = {}.toString; alert( arr.toClass() ); // [object Array]
...Но зачем копировать лишнее свойство в объект? Синтаксис
toClass.call(arr)
делает то же самое, поэтому используем его. - Всё, класс получен. При желании можно убрать обёртку `[object ...]`, взяв подстроку вызовом `slice(8,-1)`.
Метод также можно использовать с примитивами:
//+ run
alert( {}.toString.call(123) ); // [object Number]
alert( {}.toString.call("строка") ); // [object String]
[warn header="Вызов {}.toString
в консоли может выдать ошибку"]
При тестировании кода в консоли вы можете обнаружить, что если ввести в командную строку {}.toString.call(...)
-- будет ошибка. С другой стороны, вызов alert( {}.toString... )
-- работает.
Эта ошибка возникает потому, что фигурные скобки { }
в основном потоке кода интерпретируются как блок. Интерпретатор читает {}.toString.call(...)
так:
{ } // пустой блок кода
.toString.call(...) // а что это за точка в начале? не понимаю, ошибка!
Фигурные скобки считаются объектом, только если они находятся в контексте выражения. В частности, оборачивание в скобки ( {}.toString... )
тоже сработает нормально.
[/warn]
Итого
- Свойство `Class` позволяет получить тип для встроенных объектов. Далее мы будем рассматривать создание своих объектов через функцию-конструктор, с ними `Class` не работает.
- Для доступа к `Class` используется `{}.toString.call(obj).slice(8, -1)`.
Обычно в JavaScript используется "утиная" типизация. Свойство [[Class]]
-- самое надёжное средство проверки типа встроенных объектов, но обычно утиной типизации вполне хватает.