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; }