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

<b>  }</b>

<b>  // Создать перечислимые, ненастраиваемые свойства с методами доступа</b>

<b>  Object.defineProperties(this, {</b>

<b>    from: {getigetFrom, </b>

<b>      set:setFrom, </b>

<b>      enumerable:true,</b>

<b>      configurable:false},</b>

<b>    to: { get: getTo,</b>

<b>      set: setTo,</b>

<b>      enumerable:true,</b>

<b>      configurable:false }</b>

<b>  });</b>

<b>}</b>

<b>// Настройка объекта-прототипа осталась такой же, как и в предыдущих примерах.</b>

<b>// Обращение к методам экземпляров чтения свойств from и to выполняется так,</b>

<b>// как если бы они были простыми свойствами.</b>

<b>Range.prototype = hideProps({</b>

<b>  constructor: Range,</b>

<b>  includes: function(x) {</b>

<b>    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);x&lt;=this.to;x++) f(x);},</b>

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

<b>});</b>

9.8.4. Предотвращение расширения класса

Возможность расширения классов за счет добавления новых методов в объект-прототип обычно рассматривается как характерная особенность языка JavaScript. Стандарт ECMAScript 5 позволяет при желании предотвратить такую возможность. Функция

<b>Object.preventExtensions()</b>
делает объект нерасширяемым (раздел 6.8.3) - в такой объект невозможно добавить новые свойства. Функция
<b>Object.seal()</b>
идет еще дальше: она не только предотвращает добавление новых свойств, но и делает все имеющиеся свойства ненастраиваемыми, предотвращая возможность их удаления. (Однако ненастраиваемое свойство по-прежнему может быть доступно для записи и по-прежнему может быть преобразовано в свойство, доступное только для чтения.) Чтобы предотвратить возможность расширения объекта
<b>Object.prototype</b>
, можно просто записать:

<b>Object.seal(Object.prototype);</b>

Другая динамическая особенность языка JavaScript - возможность замены методов объекта:

<b>var original_sort_method = Array.prototype.sort;</b>

<b>Array.prototype.sort = function() {</b>

<b>  var start = new Date();</b>

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

<b>  var end = new Date();</b>

<b>  console.log(&quot;Сортировка массива заняла &quot; + (end - start) +</b>

<b>           &quot; миллисекунд.&quot;);</b>

<b>};</b>

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

<b>freezeProps(),</b>
объявленной выше. Другой способ добиться этого эффекта заключается в использовании функции
<b>Object.freeze(),</b>
которая выполняет те же действия, что и функция
<b>Object.seal(),</b>
и дополнительно делает все свойства ненастраиваемыми и доступными только для чтения.

Свойства, доступные только для чтения, обладают одной особенностью, о которой необходимо помнить при работе с классами. Если объект

<b>о</b>
наследует свойство
<b>р</b>
, доступное только для чтения, попытка присвоить значение свойству
<b>о.р</b>
будет завершаться неудачей без создания нового свойства в объекте
<b>о</b>
. Если потребуется переопределить унаследованное свойство, доступное только для чтения, можно воспользоваться функциями
<b>Object.defineProperty(), Object.defineProperties()</b>
или
<b>Object.create(),</b>
чтобы создать новое свойство. Это означает, что, когда методы экземпляров класса делаются доступными только для чтения, это существенно осложняет возможность их переопределения в подклассах.

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

<b>enumeration()</b>
из примера 9.7. Она сохраняет все экземпляры перечислений в свойствах объекта-прототипа и в свойстве-массиве
<b>values</b>
конструктора. Эти свойства и массив играют роль официального перечня экземпляров перечислений, и их определенно имеет смысл зафиксировать, чтобы исключить возможность добавления новых экземпляров и изменения или удаления существующих. Для этого достаточно добавить в функцию
<b>enumeration()</b>
следующие строки:

<b>Object.freeze(enumeration.values);</b>

<b>Object.freeze(enumeration);</b>

Обратите внимание, что применение функции

<b>Object.freeze()</b>
к типу перечисления исключает возможность использования свойства
<b>objectId</b>
, как было показано в примере 9.17. Решение этой проблемы состоит в том, чтобы прочитать значение свойства
<b>objectId</b>
(вызвать соответствующий метод чтения и установить внутреннее свойство) перечисления только один раз, перед тем как его зафиксировать.