JavaScript. Подробное руководство, 6-е издание, стр. 123

<b>  // Не сохраняет границы в свойствах объекта. Вместо этого определяет функции доступа,</b>

<b>  // возвращающие значения границ. Сами значения хранятся в замыкании,</b>

<b>  this.from = function() { return from; };</b>

<b>  this.to = function() { return to; };</b>

<b>}</b>

<b>// Методы прототипа не имеют прямого доступа к границам: они должны вызывать</b>

<b>// методы доступа, как любые другие функции и методы.</b>

<b>Range.prototype = { constructor: Range,</b>

<b>  includes: function(x) { return this.from() &lt;= x &amp;&amp; x &lt;= this.to(); },</b>

<b>  foreach: function(f) {</b>

<b>    for(var x=Math.ceil(this.from()), max=this.to(); x &lt;= max: x++) f(x);</b>

<b>  },</b>

<b>  toString: function() { return &quot;(&quot; + this.from() + &quot;...&quot; + this.to() + &quot;)&quot;}</b>

<b>};</b>

Новый класс

<b>Range</b>
определяет методы для чтения значений границ диапазона, но в нем отсутствуют методы или свойства для изменения этих значений. Это обстоятельство делает экземпляры этого класса неизменяемыми: при правильном использовании границы объекта
<b>Range</b>
не должны изменяться после его создания. Однако если не использовать возможности ECMAScript 5 (раздел 9.8.3), свойства
<b>from</b>
и
<b>to</b>
по-прежнему остаются доступными для записи и в действительности объекты
<b>Range</b>
не являются неизменяемыми:

<b>var r = new Range(1,5):            // &quot;неизменяемый&quot; диапазон</b>

<b>r.from = function() { return 0; }; // Изменчивость имитируется заменой метода</b>

Имейте в виду, что такой прием инкапсуляции имеет отрицательные стороны. Класс, использующий замыкание для инкапсуляции, практически наверняка будет работать медленнее и занимать больше памяти, чем эквивалент с простыми свойствами.

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

Иногда бывает необходимо реализовать возможность инициализации объектов несколькими способами. Например, можно было бы предусмотреть возможность инициализации объекта

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

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

<b>Set</b>
:

<b>function Set() {</b>

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

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

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

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

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

<b>  if (arguments.length == 1 &amp;&amp; isArraylike(arguments[0]))</b>

<b>    this.add.apply(this, arguments[0]);</b>

<b>  else</b>

<b>    if (arguments.length &gt; 0)</b>

<b>      this.add.apply(this, arguments);</b>

<b>}</b>

Такое определение конструктора

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

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

<b>Complex</b>
, инициализированный полярными координатами:

<b>Complex.polar = function(r, theta) {</b>

<b>  return new Complex(r*Math.cos(theta), r*Math.sin(theta));</b>

<b>};</b>

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

<b>Set</b>
массивом:

<b>Set.fromArray = function(a) {</b>

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

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

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

<b>};</b>

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