9.5.3. Имя конструктора
Основная проблема использования оператора instanceof или свойства constructor для определения класса объекта проявляется при наличии нескольких контекстов выполнения и, соответственно, при наличии нескольких копий функций-конструкторов. Эти функции могут быть совершенно идентичными, но разными объектами, как следствие, не равными друг другу.
Одно из возможных решений проблемы заключается в том, чтобы использовать в качестве идентификатора класса имя функции-конструктора вместо самой функции. Конструктор Array в одном окне не будет равен конструктору Array в другом окне, но их имена будут равны. Некоторые реализации JavaScript обеспечивают доступ к имени функции через нестандартное свойство name объекта функции. Для реализаций, где свойство name отсутствует, можно преобразовать функцию в строку и извлечь имя из нее. (Этот прием использовался в разделе 9.4, где демонстрировалось добавление в класс Function метода getName().)
В примере 9.4 определяется функция type(), возвращающая тип объекта в виде строки. Простые значения и функции она обрабатывает с помощью оператора typeof. Для объектов она возвращает либо значение атрибута class, либо имя конструктора. В своей работе функция type() использует функцию classof() из примера 6.4 и метод Function.getName() из раздела 9.4. Для простоты реализация этой функции и метода включена в пример.
Пример 9.4. Функция type() для определения типа значения
/**
* Возвращает тип значения в виде строки:
* -Если о - null, возвращает "null", если о - NaN, возвращает "пап”.
* -Если typeof возвращает значение, отличное от "object", возвращает это значение.
* (Обратите внимание, что некоторые реализации идентифицируют объекты
* регулярных выражений как функции.)
* -Если значение атрибута class объекта о отличается от "Object",
* возвращает это значение.
* -Если о имеет свойство constructor, а конструктор имеет имя, возвращает
* имя конструктора.
* -Иначе просто возвращает "Object".
**/
function type(o) {
var t, c, n; // type, class, name
// Специальный случай для значения null:
if (о === null) return "null":
// Другой специальный случай: NaN - единственное значение, не равное самому себе:
if (о !== о) return "nan";
// Применять typeof для любых значений, отличных от "object".
// Так идентифицируются простые значения и функции,
if ((t = typeof о) !== "object") return t;
// Вернуть класс объекта, если это не "Object".
// Так идентифицируется большинство встроенных объектов,
if ((с = classof(o)) !== "Object") return с;
// Вернуть имя конструктора объекта, если имеется
if (о.constructor && typeof о.constructor === "function" &&
(n = о.constructor.getName())) return n;
// He удалось определить конкретный тип, поэтому остается лишь
// просто вернуть "Object"
return "Object";
}
// Возвращает класс объекта,
function classof(o) {
return Object.prototype.toString.call(о).slice(8,-1);
};
// Возвращает имя функции (может быть "") или null - для объектов,
// не являющихся функциями
Function.prototype.getName = function() {
if ("name" in this) return this.name;
return this.name = this.toString().match(/functions*([^(]*)(/)[1];
):
Этот прием, основанный на использовании имени конструктора для идентификации класса объекта, имеет ту же проблему, что и прием на основе использования свойства constructor: не все объекты имеют свойство constructor. Кроме того, не все функции имеют имена. Если определить конструктор, используя выражение определения неименованной функции, метод getName() будет возвращать пустую строку:
// Этот конструктор не имеет имени
var Complex = function(x,у) { this.r = х; this.і = у; }
// Этот конструктор имеет имя
var Range = function Range(f.t) { this.from = f; this.to = t; }
Больше книг — больше знаний!
Заберите 30% скидку новым пользователям на все книги Литрес с нашим промокодом
ПОЛУЧИТЬ СКИДКУ