Прототипное программирование.

Прототипное программирование.

@frontendproglib

В отличие от большинства других ОО-языков (JavaC#), объектная система в JavaScriptоснована на прототипах, а не классах. Классы, которые вы знаете по таким языкам, как Java, технически не существуют в JavaScript (JS).

Вся иерархия объектов строиться на цепочках - прототипах. Object.prototype - объект, от которого "наследуются" все остальные объекты. Он содержит такие методы, как toString()или valueOf().Прототип у него равен null. Замечу, что Object это просто функция-конструктор для создания объектов:

typeof Object // 'function'
Object.prototype // объект Object {}
Object.prototype.__proto__ // 'null

prototype, который используется в примере, применим только к функциям, а для созданных объектов используется __proto__ (или [[Prototype]]).

Например, Array:

typeof Array // 'function'
Array.prototype.__proto__ === Object.prototype // true

var arr = [1,3]
arr.__proto__ // []
arr.__proto__ === Array.prototype // true

arr.__proto__.__proto__ // объект Object {}
arr.__proto__.__proto__ === Object.prototype // true

Все методы массивов (slice()splice()) хранятся в объекте Array.prototype, а прототип этого объекта узакывает на Object.prototype.

Получается: arr -> Array.prototype -> Object.prototype -> null

Так же с другими встроенными функциями-конструкторами, например FunctionDateNumber и т.д.

Сейчас все усложнилось ещё тем, что в новом стандарте (ES6) разработчики JS ввели class– удобный «синтаксический сахар» для задания конструктора вместе с прототипом. Насколько мне известно, его ввели в том числе специально для разработчиков, которые хотят писать на JS, но которых смущает, что в них нет классов :). На самом деле class в JS это обычная функция:

class Car {}
typeof Car // 'function'

Из плюсов прототипного наследования, наверно, это гибкость. Класс (например в Java) определяет все свойства для всех его экземпляров. Невозможно добавить свойства динамически во время выполнения. В тоже время в JS функция-конструктор определяет начальный набор свойств. Можно добавлять или удалять свойства динамически для отдельных объектов или сразу всем.

Возможно после использования интерфейсов/абстрактных классов в Java это покажется не плюсом, а минусом, но если этим уметь пользоваться, то потом этого не будет доставать в других языках.

// Функция конструктор
function Calculator () {}
Calculator.prototype.max = function (a, b) {
    return Math.max(a, b);
}

// Создадим два экземпляра (объекта)
var ins1 = new Calculator();
var ins2 = new Calculator();

// Протестируем метод max:
console.log(ins1.max(1, 5), ins2.max(1, 5)); // -> 5, 5


// Изменим метод прототипа, чтобы можно было выбирать макс. значение
// передавая сколько угодно параметров
Calculator.prototype.max = function () {
    var args = Array.prototype.slice.apply(arguments);
    return Math.max.apply(null, args);
}

// Протестируем опять:
console.log(ins1.max(1, 5), ins2.max(1, 5)); // -> 5, 5
console.log(ins1.max(9, -5, 13), ins2.max(9, -5, 13)); // -> 13, 13

Мы так же могли поменять реализацию только для одного объекта (сделать как нужно в рамках задачи).

Повторюсь, prototype, который используется в примере, применим только к функциям, а для созданных объектов используется __proto__ (или [[Prototype]]). Метод max будет находится в прототипе созданных объектов (ins1.__proto__.max), а прототипы у них указывают на один и тот же объект:

ins1.__proto__ === ins2.__proto__ // true

Интересный момент:

ins1.__proto__ // объект прототип
ins1.__proto__.constructor // function Calculator () {} - функция, с помощью который создаются объекты
ins1.__proto__.constructor.prototype // объект, который является прототипом
// Проверим?
ins1.__proto__.constructor.prototype === ins1.__proto__ // true

Если интересует момент, почему прототипное наследование в JS похоже на классическое (по синтаксису, например, использование оператора new), то это легко объяснить. Создатель JSBrendan Eich, хотел чтобы JavaScript стал младшим братом Java и пытался его сделать максимально похожим синтаксически.

Report Page