Прототипы - это оптимизация .
Отличным примером их правильного использования является библиотека jQuery. Каждый раз, когда вы получаете объект jQuery с помощью $('.someClass')
, этот объект имеет десятки «методов». Библиотека может добиться этого, вернув объект:
return {
show: function() { ... },
hide: function() { ... },
css: function() { ... },
animate: function() { ... },
};
Но это будет означать, что каждый объект jQuery в памяти будет иметь десятки именованных слотов, содержащих одни и те же методы, снова и снова.
Вместо этого эти методы определены в прототипе, и все объекты jQuery «наследуют» этот прототип, чтобы получить все эти методы с очень небольшими затратами времени выполнения.
Одна жизненно важная часть того, как jQuery делает это правильно, заключается в том, что это скрыто от программиста. Это рассматривается исключительно как оптимизация, а не как то, о чем вы должны беспокоиться при использовании библиотеки.
Проблема с JavaScript заключается в том, что функции открытого конструктора требуют, чтобы вызывающий не забыл поставить перед ними префикс, new
иначе они обычно не работают. Для этого нет веской причины. jQuery $
делает все правильно, скрывая эту бессмыслицу за обычной функцией , поэтому вам не нужно заботиться о том, как реализованы объекты.
Чтобы вы могли легко создать объект с указанным прототипом, ECMAScript 5 включает стандартную функцию Object.create
. Его сильно упрощенная версия выглядела бы так:
Object.create = function(prototype) {
var Type = function () {};
Type.prototype = prototype;
return new Type();
};
Он просто позаботится о том, чтобы написать функцию-конструктор и затем вызвать ее с помощью new
.
Когда бы вы избегали прототипов?
Полезно сравнить с популярными объектно-ориентированными языками, такими как Java и C #. Они поддерживают два типа наследования:
- Интерфейс наследование, где вы , что класс предоставляет свою собственную уникальную реализацию для каждого члена интерфейса.
implement
interface
- Реализация наследования, где вы , что обеспечивает по умолчанию реализации некоторых методов.
extend
class
В JavaScript прототипное наследование - это своего рода наследование реализации . Таким образом, в тех ситуациях, когда (в C # или Java) вы должны были бы унаследовать от базового класса, чтобы получить поведение по умолчанию, в которое вы затем вносите небольшие изменения через переопределения, тогда в JavaScript прототипное наследование имеет смысл.
Однако, если вы находитесь в ситуации, когда вы использовали бы интерфейсы на C # или Java, вам не нужна какая-либо конкретная языковая функция в JavaScript. Нет необходимости явно объявлять что-то, что представляет интерфейс, и нет необходимости отмечать объекты как «реализующие» этот интерфейс:
var duck = {
quack: function() { ... }
};
duck.quack();
Другими словами, если каждый «тип» объекта имеет свои собственные определения «методов», тогда нет никакого смысла в наследовании от прототипа. После этого это зависит от того, сколько экземпляров вы выделяете каждого типа. Но во многих модульных конструкциях есть только один экземпляр данного типа.
Фактически, многие люди предполагали, что наследование реализации - зло . То есть, если есть некоторые общие операции для типа, тогда, возможно, будет понятнее, если они не помещены в базовый / суперкласс, а вместо этого просто представлены как обычные функции в каком-то модуле, которому вы передаете объект (ы) вы хотите, чтобы они оперировали.