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

<b>  var props = (arguments.length == 1) // Если один аргумент,</b>

<b>     ? Object.getOwnPropertyNames(o) // изменить все свойства,</b>

<b>     : Array.prototype.splice.call(arguments, 1);</b>
<b>  // иначе только указанные</b>

<b>  props.forEach(function(n) { // Делает каждое свойство ненастраиваемым</b>

<b>    // и доступным только для чтения</b>

<b>    // Пропустить ненастраиваемые свойства</b>

<b>    if (!Object.getOwnPropertyDescriptor(o,n).configurable) return:</b>

<b>    Object.defineProperty(o, n, { writable: false, configurable: false });</b>

<b>  }):</b>

<b>  return о; // Чтобы можно было продолжить работу с объектом о</b>

<b>}</b>

<b>// Делает неперечислимыми указанные (или все) свойства объекта о,</b>

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

<b>function hideProps(o) {</b>

<b>  var props = (arguments.length == 1) // Если один аргумент,</b>

<b>    ? Object.getOwnPropertyNames(o) // изменить все свойства,</b>

<b>    : Array.prototype.splice.call(arguments, 1);</b>

<b>  // иначе только указанные</b>

<b>  props.forEach(function(n) { // Скрыть каждое от цикла for/in</b>

<b>    // Пропустить ненастраиваемые свойства</b>

<b>    if (!Object.getOwnPropertyDescriptor(o,n).configurable) return:</b>

<b>    Object.defineProperty(o, n, { enumerable: false });</b>

<b>  }):</b>

<b>  return o;</b>

<b>}</b>

Функции

<b>Object.defineProperty()</b>
и
<b>Object.defineProperties()</b>
могут использоваться и для создания новых свойств, и для изменения атрибутов уже существующих свойств. При создании новых свойств все опущенные атрибуты по умолчанию принимают значение
<b>false</b>
. Однако при изменении атрибутов уже существующих свойств опущенные атрибуты не изменяются. Например, в функции
<b>hideProps()</b>
выше указывается только атрибут
<b>enumerable</b>
, потому что функция должна изменять только его.

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

<b>Range</b>
, в котором используются наши вспомогательные функции.

Пример 9.20. Более простое определение неизменяемого класса

<b>function Range(from, to) { // Конструктор неизменяемого класса Range</b>

<b>  this.from = from;</b>

<b>  this.to = to;</b>

<b>  freezeProps(this); // Сделать свойства неизменяемыми</b>

<b>}</b>

<b>Range.prototype = hideProps({ // Определить неперечислимые свойства прототипа</b>

<b>  constructor: Range,</b>

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

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

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

<b>});</b>

9.8.3. Сокрытие данных объекта

В разделе 9.6.6 и в примере 9.10 было показано, как можно использовать переменные и аргументы функции-конструктора для сокрытия данных объекта, создаваемого этим конструктором. Недостаток этого приема заключается в том, что в ECMAScript 3 допускается возможность замещения методов доступа к этим данным. Стандарт ECMAScript 5 позволяет обеспечить более надежное сокрытие частных данных за счет определения методов доступа к свойствам, которые не могут быть удалены. Этот способ демонстрируется в примере 9.21.

Пример 9.21. Класс Range со строго инкапсулированными границами

<b>// Эта версия класса Range является изменяемой, но она следит за своими</b>

<b>// границами, обеспечивая выполнение условия from &lt;= to.</b>

<b>function Range(from, to) {</b>

<b>  // Проверить соблюдение условия при создании</b>

<b>  if (from &gt; to) throw new Error(&quot;Range: значение from должно быть &lt;= to&quot;);</b>

<b>  // Определение методов доступа, которые следят за соблюдением условия</b>

<b>  function getFrom() { return from; }</b>

<b>  function getTo() { return to; }</b>

<b>  function setFrom(f) { // He позволяет устанавливать значение from &gt; to</b>

<b>    if (f &lt;= to) from = f;</b>

<b>    else throw new Error(&quot;Range: значение from должно быть &lt;= to&quot;);</b>

<b>  }</b>

<b>  function setTo(t) { // He позволяет устанавливать значение to &lt; from</b>

<b>    if (t &gt;= from) to = t;</b>

<b>    else throw new Error(&quot;Range: значение to должно быть &gt;= from&quot;);</b>