Пример 9.20. Более простое определение неизменяемого класса
function Range(from, to) { // Конструктор неизменяемого класса Range
this.from = from;
this.to = to;
freezeProps(this); // Сделать свойства неизменяемыми
}
Range.prototype = hideProps({ // Определить неперечислимые свойства прототипа
constructor: Range,
includes: function(x) { return this.from <= x && x <= this.to; },
foreach: function(f) {for(var x=Math.ceil(this.from);x<=this.to;x++) f(x);},
toString: function() { return "(" + this.from + "..." + this.to + ")"; }
});
9.8.3. Сокрытие данных объекта
В разделе 9.6.6 и в примере 9.10 было показано, как можно использовать переменные и аргументы функции-конструктора для сокрытия данных объекта, создаваемого этим конструктором. Недостаток этого приема заключается в том, что в ECMAScript 3 допускается возможность замещения методов доступа к этим данным. Стандарт ECMAScript 5 позволяет обеспечить более надежное сокрытие частных данных за счет определения методов доступа к свойствам, которые не могут быть удалены. Этот способ демонстрируется в примере 9.21.
Пример 9.21. Класс Range со строго инкапсулированными границами
// Эта версия класса Range является изменяемой, но она следит за своими
// границами, обеспечивая выполнение условия from <= to.
function Range(from, to) {
// Проверить соблюдение условия при создании
if (from > to) throw new Error("Range: значение from должно быть <= to");
// Определение методов доступа, которые следят за соблюдением условия
function getFrom() { return from; }
function getTo() { return to; }
function setFrom(f) { // He позволяет устанавливать значение from > to
if (f <= to) from = f;
else throw new Error("Range: значение from должно быть <= to");
}
function setTo(t) { // He позволяет устанавливать значение to < from
if (t >= from) to = t;
else throw new Error("Range: значение to должно быть >= from");
}
// Создать перечислимые, ненастраиваемые свойства с методами доступа
Object.defineProperties(this, {
from: {getigetFrom,
set:setFrom,
enumerable:true,
configurable:false},
to: { get: getTo,
set: setTo,
enumerable:true,
configurable:false }
});
}
// Настройка объекта-прототипа осталась такой же, как и в предыдущих примерах.
// Обращение к методам экземпляров чтения свойств from и to выполняется так,
// как если бы они были простыми свойствами.
Range.prototype = hideProps({
constructor: Range,
includes: function(x) {
return this.from <= x && x <= this.to: },
foreach: function(f) {
for(var x=Math.ceil(this.from);x<=this.to;x++) f(x);},
toString: function() { return "(" + this, from + ”..." + this, to + ")"; }
});
9.8.4. Предотвращение расширения класса
Возможность расширения классов за счет добавления новых методов в объект-прототип обычно рассматривается как характерная особенность языка JavaScript. Стандарт ECMAScript 5 позволяет при желании предотвратить такую возможность. Функция Object.preventExtensions()
делает объект нерасширяемым (раздел 6.8.3) - в такой объект невозможно добавить новые свойства. Функция Object.seal()
идет еще дальше: она не только предотвращает добавление новых свойств, но и делает все имеющиеся свойства ненастраиваемыми, предотвращая возможность их удаления. (Однако ненастраиваемое свойство по-прежнему может быть доступно для записи и по-прежнему может быть преобразовано в свойство, доступное только для чтения.) Чтобы предотвратить возможность расширения объекта Object.prototype
, можно просто записать:
Object.seal(Object.prototype);
Другая динамическая особенность языка JavaScript - возможность замены методов объекта:
var original_sort_method = Array.prototype.sort;
Array.prototype.sort = function() {
var start = new Date();
original_sort_method.apply(this, arguments);
var end = new Date();
console.log("Сортировка массива заняла " + (end - start) +
" миллисекунд.");
};
Читать дальше
Конец ознакомительного отрывка
Купить книгу