Выбрать главу

Эта глава описывает довольно эксцентричный подход JavaScript к объектам, и то, как они соотносятся с классическими объектно-ориентированными техниками.

Методы

Методы – свойства, содержащие функции. Простой метод:

var rabbit = {};

rabbit.speak = function(line) {

  console.log("Кролик говорит '" + line + "'");

};

rabbit.speak("Я живой.");

// → Кролик говорит 'Я живой.'

Обычно метод должен что-то сделать с объектом, через который он был вызван. Когда функцию вызывают в виде метода – как свойство объекта, например object.method() – специальная переменная в её теле будет указывать на вызвавший её объект.

function speak(line) {

  console.log("А " + this.type + " кролик говорит '" + line + "'");

}

var whiteRabbit = {type: "белый", speak: speak};

var fatRabbit = {type: "толстый", speak: speak};

whiteRabbit.speak("Ушки мои и усики, я же наверняка опаздываю!");

// → А белый кролик говорит 'Ушки мои и усики, я же наверняка опаздываю!'

fatRabbit.speak("Мне бы сейчас морковочки.");

// → А толстый кролик говорит 'Мне бы сейчас морковочки.'

Код использует ключевое слово this для вывода типа говорящего кролика.

Вспомните, что методы apply и bind принимают первый аргумент, который можно использовать для эмуляции вызова методов. Этот первый аргумент как раз даёт значение переменной this.

Есть метод, похожий на apply, под названием call. Он тоже вызывает функцию, методом которой является, только принимает аргументы как обычно, а не в виде массива. Как apply и bind, в call можно передать значение this.

speak.apply(fatRabbit, ["Отрыжка!"]);

// → А толстый кролик говорит 'Отрыжка!'

speak.call({type: "старый"}, "О, господи.");

// → А старый кролик говорит 'О, господи.'

Прототипы

Следите за руками.

var empty = {};

console.log(empty.toString);

// → function toString(){…}

console.log(empty.toString());

// → [object Object]

Я достал свойство пустого объекта. Магия!

Ну, не магия, конечно. Я просто не всё рассказал про то, как работают объекты в JavaScript. В дополнение к набору свойств, почти у всех также есть прототип. Прототип – это ещё один объект, который используется как запасной источник свойств. Когда объект получает запрос на свойство, которого у него нет, это свойство ищется у его прототипа, затем у прототипа прототипа, и т. д.

Ну а кто же прототип пустого объекта? Это великий предок всех объектов, Object.prototype.

console.log(Object.getPrototypeOf({}) == Object.prototype);

// → true

console.log(Object.getPrototypeOf(Object.prototype));

// → null

Как и следовало ожидать, функция Object.getPrototypeOf возвращает прототип объекта.

Прототипические отношения в JavaScript выглядят как дерево, в корне которого находится Object.prototype. Он предоставляет несколько методов, которые появляются у всех объектов. Например, toString, который преобразует объект в строковый вид.

Прототипом многих объектов служит не непосредственно Object.prototype, а какой-то другой объект, который предоставляет свои свойства по умолчанию. Функции происходят от Function.prototype, массивы – от Array.prototype.

console.log(Object.getPrototypeOf(isNaN) == Function.prototype);

// → true

console.log(Object.getPrototypeOf([]) == Array.prototype);

// → true

У таких прототипов будет свой прототип – часто Object.prototype, поэтому он всё равно, хоть и не напрямую, предоставляет им методы типа toString.

Функция Object.getPrototypeOf возвращает прототип объекта. Можно использовать Object.create для создания объектов с заданным прототипом.

var protoRabbit = {

  speak: function(line) {

    console.log("А " + this.type + " кролик говорит '" + line + "'");

  }

};

var killerRabbit = Object.create(protoRabbit);

killerRabbit.type = "убийственный";

killerRabbit.speak("ХРЯЯЯСЬ!");

// → А убийственный кролик говорит 'ХРЯЯЯСЬ!'

Прото-кролик работает в качестве контейнера свойств, которые есть у всех кроликов. Конкретный объект-кролик, например убийственный, содержит свойства, применимые только к нему – например, свой тип – и наследует разделяемые с другими свойства от прототипа.

Конструкторы

Более удобный способ создания объектов, наследуемых от некоего прототипа – конструктор. В JavaScript вызов функции с предшествующим ключевым словом new приводит к тому, что функция работает как конструктор. Конструктор создает новый объект и возвращает его, если только явно не задано возвращение другого объекта вместо созданного. При этом свежесозданный объект доступен изнутри конструктора через переменную this.