Недостаток оператора instanceof и свойства constructor заключается в том, что они позволяют проверять объекты на принадлежность только известным вам классам, но не дают никакой полезной информации при исследовании неизвест_ ных объектов, что может потребоваться, например, при отладке. В такой ситуа_ ции на помощь может прийти метод Object.toString().
Как уже говорилось в главе 7, класс Object содержит определение метода toString() по умолчанию. Любой класс, который не определяет собственный ме_ тод, наследует реализацию по умолчанию. Интересная особенность метода по умолчанию toString() состоит в том, что он выводит некоторую внутреннюю ин_ формацию о типе встроенных объектов. Спецификация ECMAScript требует, чтобы метод по умолчанию toString() всегда возвращал строку в формате:
[object class]
Здесь class – это внутренний тип объекта, который обычно соответствует имени функции_конструктора этого объекта. Например, для массивов class – это "Array", для функций – "Function", и для объектов даты/времени – "Date". Для встроенно_ го класса Math возвращается "Math", а для всех классов семейства Error – строка "Error". Для объектов клиентского языка JavaScript и любых других объектов, определяемых реализацией JavaScript, в качестве строки class возвращается строка, определяемая реализацией (например, "Window", "Document" или "Form"). Однако для типов объектов, определяемых пользователем, таких как Circle и Complex, описанных ранее в этой главе, в качестве строки class всегда возвра_ щается строка "Object". То есть метод toString() способен определять только встроенные типы объектов.
Поскольку в большинстве классов метод по умолчанию toString() переопределя_ ется, не следует ожидать, что вызвав его непосредственно из объекта, вы полу_ чите имя класса. Поэтому необходимо обращаться к функции по умолчанию Ob_ ject.prototype явно и использовать для этого метод apply() с указанием объекта, тип которого требуется узнать:
Object.prototype.toString.apply(o); // Всегда вызывается метод по умолчанию toString()
Этот прием используется в примере 9.6 в определении функции, реализующей расширенные возможности по выяснению типа. Как уже отмечалось ранее, ме_ тод toString() не работает с пользовательскими классами, в этом случае показан_ ная далее функция проверяет строковое значение свойства classname и возвраща_ ет его значение, если оно определено.
Пример 9.6. Улучшенные возможности определения типа
function getType(x) {
// Если значение x равно null, возвращается "null" if (x == null) return "null";
// Попробовать определить тип с помощью оператора typeof var t = typeof x;
9.7. Определение типа объекта
// Если получен непонятный результат, вернуть его if (t != "object") return t;
// В противном случае, x – это объект. Вызвать метод toString()
// по умолчанию и извлечь подстроку с именем класса.
var
c = Object.prototype.toString.apply(x); //
В формате "[object class]"
c =
c.substring(8, c.length_1);
//
Удалить "[object" и "]"
// Если имя класса _ не Object, вернуть его. if (c != "Object") return c;
// Если получен тип "Object", проверить, может быть x
// действительно принадлежит этому классу.
if (x.constructor == Object) return c; // Тип действительно "Object"
// Для пользовательских классов извлечь строковое значение свойства
// classname, которое наследуется от объекта_прототипа
if ("classname" in x.constructor.prototype &&
// наследуемое имя класса
typeof
x.constructor.prototype.classname == "string")
// это строка
return
x.constructor.prototype.classname;
// Если определить тип так и не удалось, так и скажем об этом. return "<unknown type>";