Выразительный JavaScript (Хавербеке) - страница 66

и >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 находится другая функция.

Такая схема называется наследованием. Мы можем строить в чём-то отличные типы данных на основе существующих, не тратя много сил. Обычно новый конструктор вызывает старый (через метод