en.javascript.info/1-js/7-js-misc/1-class-property/article.md
2015-01-14 10:23:45 +03:00

5.8 KiB
Raw Blame History

Секретное свойство 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

Разберем происходящее более подробно.

  1. Можно переписать эту строку в две:
    var obj = {};
    var toClass = obj.toString;
    

    Иначе говоря, мы создаём пустой объект {} и копируем ссылку на его метод toString в переменную toClass.

    Для получения [[Class]] нужна именно внутренняя реализация toString стандартного объекта Object, другая не подойдёт.

  2. Вызываем скопированный метод в контексте нужного объекта `obj`.

    Мы могли бы поступить проще -- одолжить метод под другим названием:

    //+ run
    var arr = [1,2];
    arr.toClass = {}.toString;
    
    alert( arr.toClass() ); // [object Array]
    

    ...Но зачем копировать лишнее свойство в объект? Синтаксис toClass.call(arr) делает то же самое, поэтому используем его.

  3. Всё, класс получен. При желании можно убрать обёртку `[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]] -- самое надёжное средство проверки типа встроенных объектов, но обычно утиной типизации вполне хватает.