Прототипы

В листинге 11.1 мы определяли метод getModel() внутри конструктора:

function Car(m, y) { // Конструктор класса
   this.model = m;
   this.year = y;
   this.getModel = function() {
      return this.model;
   }
}

Подобное решение не является эффективным. Предположим, необходимо составить массив, описывающий тысячу автомобилей. Свойства model и year в этом случае будут содержать разные значения, а вот метод getModel() во всех этих объектах один и тот же, но создавать мы его будем тысячу раз.

Использование прототипов позволяет определить метод вне конструктора. При создании объекта наследуются все свойства, которые имеются в прототипе. Таким образом, метод getModel() будет определен один раз, но будет наследоваться всеми экземплярами.

Для добавления метода в прототип используется свойство prototype (листинг 11.2).

Листинг 11.2. Прототипы

function Car(m, y) {
   this.model = m;
   this.year = y;
}
Car.prototype.getModel = function() {
   return this.model;
}
// Создание экземпляров
let obj1 = new Car('ВАЗ-2109', 2007);
let obj2 = new Car('LADA Granta', 2014);
// Вывод значений
console.log(obj1.year);       // 2007
console.log(obj1.getModel()); // ВАЗ-2109
console.log(obj2.year);       // 2014
console.log(obj2.getModel()); // LADA Granta

Как уже говорилось, свойства, определенные в прототипе, наследуются всеми экземплярами. Таким образом, метод getModel() доступен для перебора в цикле for...in, а также успешно проверяется на наличие с помощью оператора in. Тем не менее метод hasOwnProperty() позволяет определить, что метод является унаследованным:

console.log( 'getModel' in obj1 );              // true
console.log( obj1.hasOwnProperty('getModel') ); // false

Как вы уже знаете, все объекты имеют свойство __proto__, ссылающееся на прототип объекта. В свою очередь все функции и классы содержат свойство prototype, которое также ссылается на прототип. Эти два свойства ссылаются на один и тот же прототип, если объект создан на основе функции или класса:

console.log(obj1.__proto__ === Car.prototype);  // true
console.log(obj2.__proto__ === Car.prototype);  // true
console.log(Object.getPrototypeOf(obj1) === Car.prototype);
// true

Функции и классы являются объектами, поэтому они также содержат свойство __proto__, которое ссылается на прототип класса Function:

console.log(Car.__proto__ === Function.prototype);    // true
console.log(
  Function.prototype.__proto__ === Object.prototype); // true

Почти все встроенные классы JavaScript (например, String, Array) имеют свойство prototype. С его помощью можно расширить возможности встроенных классов, например, добавить новый метод. В качестве примера добавим метод inArray() в класс Array. Этот метод будет производить поиск значения в массиве и возвращать индекс первого вхождения. Если вхождение не найдено, то метод вернет значение -1:

Array.prototype.inArray = function(elem) {
   for (let i = 0, len = this.length; i < len; i++) {
      if (this[i] === elem) return i;
   }
   return -1;
}
let arr = [ 1, 2, 3, 4, 5, 1 ];
let pos = arr.inArray(5);
if (pos !== -1) console.log(`Индекс элемента ${pos}`);
else console.log('Не найдено');

Если добавить метод в прототип класса Object, то он будет доступен всем объектам:

Object.prototype.printHello = function() {
   console.log('Hello');
}
let arr = [ 1, 2, 3, 4, 5, 1 ];
arr.printHello();    // Hello
let obj = { a: 1 };
obj.printHello();    // Hello
let x = 4;
x.printHello();      // Hello
Примечание

Учебник по Node.js и JavaScript
Учебник по JavaScript (Node.js) в формате PDF

Помощь сайту

ЮMoney (Yandex-деньги): 410011140483022

ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов