и
>setSomething
для чтения и записи значений свойств. Но в таком подходе есть и минус – приходится писать (и читать) много дополнительных методов.
К счастью, JavaScript даёт нам технику, использующую лучшее из обоих подходов. Мы можем задать свойства, которые снаружи выглядят обыкновенными, но втайне имеют связанные с ними методы.
>var pile = {
> elements: ["скорлупа", "кожура", "червяк"],
> get height() {
> return this.elements.length;
> },
> set height(value) {
> console.log("Игнорируем попытку задать высоту", value);
> }
>};
>console.log(pile.height);
>// → 3
>pile.height = 100;
>// → Игнорируем попытку задать высоту 100
В объявлении объекта записи >get
или >set
позволяют задать функцию, которая будет вызвана при чтении или записи свойства. Можно также добавить такое свойство в существующий объект, к примеру, в >prototype
, используя функцию >Object.defineProperty
(раньше мы её уже использовали, создавая несчётные свойства).
>Object.defineProperty(TextCell.prototype, "heightProp", {
> get: function() { return this.text.length; }
>});
>var cell = new TextCell("да\nну");
>console.log(cell.heightProp);
>// → 2
>cell.heightProp = 100;
>console.log(cell.heightProp);
>// → 2
Так же можно задавать свойство >set
в объекте, передаваемом в >defineProperty
, для задания метода-сеттера. Когда геттер есть, а сеттера нет, попытка записи в свойство просто игнорируется.
Но мы ещё не закончили с нашим упражнением по форматированию таблицы. Читать её было бы удобнее, если б числовой столбец был выровнен по правому краю. Нам нужно создать ещё один тип ячеек вроде >TextCell
, но чтобы текст дополнялся пробелами слева, а не справа — для выравнивания по правому краю.
Мы могли бы написать новый конструктор со всеми тремя методами в прототипе. Но прототипы могут сами иметь прототипы, и поэтому мы можем поступить умнее.
>function RTextCell(text) {
> TextCell.call(this, text);
>}
>RTextCell.prototype = Object.create(TextCell.prototype);
>RTextCell.prototype.draw = function(width, height) {
> var result = [];
> for (var i = 0; i < height; i++) {
> var line = this.text[i] || "";
> result.push(repeat(" ", width - line.length) + line);
> }
> return result;
>};
Мы повторно использовали конструктор и методы >minHeight
и >minWidth
из обычного >TextCell
. И >RTextCell
теперь в общем эквивалентен >TextCell
, за исключением того, что в методе >draw
находится другая функция.
Такая схема называется наследованием. Мы можем строить в чём-то отличные типы данных на основе существующих, не тратя много сил. Обычно новый конструктор вызывает старый (через метод