Я думаю, что главная причина того, что мы не видим больше прототипа, заключается в том, что синтаксис по умолчанию в Javascript - это псевдоклассическая аберрация вместо более благоприятного Object.create. Если вы хотите по-настоящему увидеть прототипный блеск, ищите места, где используется эта функция. Следующий пример взят из инструментария Dojo:
Предостережение : Я как бы изменил свое мнение о том, насколько хорош этот вид кода с тех пор, как я первоначально написал этот ответ. Хотя основная идея остается в силе, вы должны быть осторожны, если у вас есть методы, которые изменяют свойства экземпляра ("this"). Это потому, что если вы вызываете метод в объекте делегата через делегатор, то вы можете в конечном итоге установить переменные в делегаторе, а не в делегате, и это может нарушить некоторые инварианты, если кто-то в итоге получит доступ к делегату, непосредственно последнему.
Вся идея в том, что у вас на 100% все в порядке.
Dojo определяет общий интерфейс store (с такими методами, как get (), add () и т. Д.), Который можно использовать, например, для абстрагирования REST API от сервера. Мы хотели бы создать функцию Cache , которая получает любое хранилище данных и возвращает новую версию, которая кэширует любые вызовы метода get () (это позволяет нам отделить кэширование от поведения хранилища при реализации фактический get ())
Первая идея заключается в использовании факта, что Javascript является высоко динамичным, для замены метода get:
//not the actual implementation. Things get more complicated w/ async code.
var oldGet = store.get;
store.get = function(id){
if(!cache[id]){ cache[id] = oldGet(id); }
return cache[id];
}
Однако, это удлиняет исходный объект, поэтому вы больше не можете получить доступ к исходному методу, а также усложняет добавление других модификаций параллельно.
Вторая идея заключается в создании более надежного решения с использованием делегирования:
function Cache(obj){
this.obj = obj;
}
Cache.prototype = {
get: function(){
//do things involving this.obj...
}
};
Это выглядит многообещающе, пока вы не вспомните, что полученный объект Cache должен реализовать интерфейс хранилища. Мы можем попробовать добавить все методы вручную:
Cache.prototype = {
//...
put: function(){ return this.obj.apply(this, arguments); },
//...
}
но это не только громоздко и чревато ошибками (так легко что-то забыть), оно даже не будет таким мощным, как решение для модификации объектов, поскольку мы теряем доступ к методам в этом исходном объекте, которые не из интерфейс магазина.
Что ж, способ сделать это "автоматическое делегирование" - это наследование, но в этом случае оно изначально кажется бесполезным, поскольку вам нужно будет создать новый подкласс кэша для каждого возможного класса хранилища, или вам понадобится какой-то вид Необычный миксин множественного наследования. Введите прототип наследования, чтобы сохранить день. Мы можем легко создать новый объект, который добавляет функциональность к старому , не изменяя его и не манипулируя классом hieharchies
dojo.store.Cache = function(masterStore, cachingStore, options){
//...
return dojo.delegate(masterStore, {
//...
get: function(id, directives){
//...
}
//...
}
}
Где dojo.delegate - это функция, которая создает новый объект со всеми свойствами во втором аргументе и прототип которого будет первым аргументом.
Теоретические несоответствия JS: наследование прототипов может использоваться еще более агрессивно в сценариях с еще большим делегированием в таком языке, как Self, который допускает несколько прототипов, а также прямой доступ и модификацию прототипов во время выполнения. Например, можно реализовать шаблон State из GoF, делегировав все подходящие методы прототипу и меняя прототип при каждом изменении состояния.