Прототипи - це оптимізація .
Прекрасним прикладом їх хорошого використання є бібліотека jQuery. Щоразу, коли ви отримуєте об'єкт jQuery, використовуючи $('.someClass')
цей об'єкт має десятки "методів". Бібліотека могла досягти цього, повернувши об’єкт:
return {
show: function() { ... },
hide: function() { ... },
css: function() { ... },
animate: function() { ... },
// etc...
};
Але це означало б, що кожен об'єкт jQuery в пам'яті мав би десятки іменованих слотів, що містять однакові методи, знову і знову.
Натомість ці методи визначаються на прототипі, і всі об'єкти jQuery "успадковують" цей прототип, щоб отримати ці методи з дуже невеликою вартістю виконання.
Одним із життєво важливих моментів того, як jQuery отримує все правильно, є те, що це приховано від програміста. Це трактується суто як оптимізація, а не як те, про що вам слід турбуватися, користуючись бібліотекою.
Проблема JavaScript полягає в тому, що функції оголеного конструктора вимагають від абонента пам’ятати про те, щоб додати їм префікс, new
інакше вони зазвичай не працюють. Для цього немає вагомих причин. jQuery робить це правильно, приховуючи цю нісенітницю за звичайною функцією $
, тому вам не потрібно дбати про те, як реалізовані об'єкти.
Щоб ви могли зручно створювати об'єкт із зазначеним прототипом, ECMAScript 5 включає стандартну функцію Object.create
. Значно спрощена його версія буде виглядати так:
Object.create = function(prototype) {
var Type = function () {};
Type.prototype = prototype;
return new Type();
};
Він просто піклується про біль при написанні функції конструктора, а потім викликає її за допомогою new
.
Коли б ви уникали прототипів?
Корисне порівняння з популярними мовами OO, такими як Java та C #. Вони підтримують два види успадкування:
- Інтерфейс успадкування, де ви , що клас надає свою власну унікальну реалізацію для кожного члена інтерфейсу.
implement
interface
- Реалізація успадкування, де ви , що забезпечує по замовчуванням реалізації деяких методів.
extend
class
У JavaScript прототипне успадкування є різновидом успадкування реалізації . Отже, у тих ситуаціях, коли (у C # або Java) ви отримали б базовий клас, щоб отримати поведінку за замовчуванням, до якої ви потім вносите невеликі зміни за допомогою перевизначень, тоді в JavaScript прототипове успадкування має сенс.
Однак, якщо ви потрапили в ситуацію, коли б ви використовували інтерфейси на C # або Java, тоді вам не потрібна жодна особлива мовна функція в JavaScript. Немає необхідності явно оголошувати щось, що представляє інтерфейс, і не потрібно позначати об'єкти як "реалізуючі" цей інтерфейс:
var duck = {
quack: function() { ... }
};
duck.quack(); // we're satisfied it's a duck!
Іншими словами, якщо кожен "тип" об'єкта має власні визначення "методів", тоді спадкування від прототипу не має значення. Після цього це залежить від того, скільки екземплярів ви виділите для кожного типу. Але в багатьох модульних конструкціях існує лише один примірник даного типу.
Насправді багато людей припускають, що реалізація успадкування є злом . Тобто, якщо є деякі загальні операції для типу, то, можливо, це зрозуміліше, якщо вони не введені в базовий / супер клас, а натомість просто виставляються як звичайні функції в якомусь модулі, якому ви передаєте об’єкт (и) ви хочете, щоб вони оперували.