9.6.7. Перегрузка конструкторов и фабричные методы

We use cookies. Read the Privacy and Cookie Policy

Иногда бывает необходимо реализовать возможность инициализации объектов несколькими способами. Например, можно было бы предусмотреть возможность инициализации объекта Complex значениями радиуса и угла (полярные координаты) вместо вещественной и мнимой составляющих. Или создавать объекты множеств Set, членами которых являются элементы массива, а не аргументы конструктора.

Один из способов реализовать такую возможность заключается в создании перегруженных версий конструктора и выполнении в них различных способов инициализации в зависимости от передаваемых аргументов. Ниже приводится пример перегруженной версии конструктора Set:

function Set() {

  this.values = {}; // Свойство для хранения множества

  this.n =0; // Количество значений в множестве

  // Если конструктору передается единственный объект, подобный массиву,

  // он добавляет элементы массива в множество.

  // В противном случае в множество добавляются все аргументы

  if (arguments.length == 1 && isArraylike(arguments[0]))

    this.add.apply(this, arguments[0]);

  else

    if (arguments.length > 0)

      this.add.apply(this, arguments);

}

Такое определение конструктора Set() позволяет явно перечислять элементы множества в вызове конструктора или передавать ему массив элементов множества. К сожалению, этот конструктор имеет одну неоднозначность: его нельзя использовать для создания множества, содержащего единственный элемент-массив. (Для этого потребуется создать пустое множество, а затем явно вызвать метод add().)

В случае с комплексными числами реализовать перегруженную версию конструктора, которая инициализирует объект полярными координатами, вообще невозможно. Оба представления комплексных чисел состоят из двух вещественных чисел. Если не добавить в конструктор третий аргумент, он не сможет по своим аргументам определить, какое представление следует использовать. Однако вместо этого можно написать фабричный метод - метод класса, возвращающий экземпляр класса. Ниже приводится фабричный метод, возвращающий объект Complex, инициализированный полярными координатами:

Complex.polar = function(r, theta) {

  return new Complex(r*Math.cos(theta), r*Math.sin(theta));

};

А так можно реализовать фабричный метод для инициализации объекта Set массивом:

Set.fromArray = function(a) {

  s = new Set(); // Создать пустое множество

  s.add.apply(s, a); // Передать элементы массива методу add

  return s; // Вернуть новое множество

};

Привлекательность фабричных методов заключается в том, что им можно дать любые имена, а методы с разными именами могут выполнять разные виды инициализации. Поскольку конструкторы играют роль имен классов, обычно в классах определяется единственный конструктор. Однако это не является непреложным правилом. В языке JavaScript допускается определять несколько функций-конструкторов, использующих общий объект-прототип, и в этом случае объекты, созданные разными конструкторами одного класса, будут иметь один и тот же тип. Данный прием не рекомендуется к использованию, тем не менее ниже приводится вспомогательный конструктор такого рода:

// Вспомогательный конструктор для класса Set.

function SetFromArray(a) {

  // Инициализировать новый объект вызовом конструктора Set() как функции,

  // передав ей элементы массива в виде отдельных аргументов.

  Set.apply(this, а);

}

// Установить прототип, чтобы функция SetFromArray создавала экземпляры Set

SetFromArray.prototype = Set.prototype;

var s = new SetFromArray([1,2,3]);

s instanceof Set // => true

В ECMAScript 5 функции имеют метод bind(), особенности которого позволяют создавать подобные вспомогательные конструкторы (раздел 8.7.4).