Я только начал узнавать о Прокси и Рефлексии, и был бы признателен за некоторые указания в правильном направлении, так как я немного заблудился здесь. Моя задача - создать класс Wand
, который принимает объект функций (заклинаний). Каждый вызов заклинания должен добавлять имя заклинания в массив истории, который содержит журналы для самых последних вызовов (до 3).
Новый экземпляр также должен позволять использование методов prioriIncantatem()
и deletrius()
(но они не должны присутствовать в самом экземпляре / классе), первый вернет текущий массив истории (до добавления prioriIncantatem
к нему), второй очистит историю. Эти методы также должны быть зарегистрированы в массиве истории (prioriIncantatem
будет отображаться только при следующем вызове, но все еще будет регистрироваться). Экземпляр должен наследоваться от Object.prototype
, поэтому такие вещи, как .toString()
, работают как обычно, что приводит к [object Object]
(они также будут записаны в историю).
Экземпляр также должен позволять динамически добавлять новые методы ( заклинания), будучи в состоянии вызывать существующие методы, используя this
, который будет регистрировать оба обращения к массиву истории и работать с другими методами, как если бы они все были созданы при создании.
Это должно работать так:
const w = new Wand({ alohomora: function() { console.log('unlocked!') },});
w.alohomora(); // logs 'unlocked!'
w.prioriIncantatem(); // => ['alohomora']
w.myNewSpell = function() { console.log('magic!'); }
w.myNewSpell(); // logs 'magic!'
w.prioriIncantatem(); // => ['myNewSpell', 'prioriIncantatem', 'alohomora']
w.deletrius();
w.prioriIncantatem(); // => ['deletrius']
Object.getOwnPropertyNames(w) == ["alohomora", "_history", "myNewSpell"]
Не совсем понял, как обернуть экземпляр прокси, так что после небольшого исследования я пришел к этому, что, похоже, работает с приведенным выше примером
class WandClass {
constructor(opts = {}) {
Object.assign(this, opts);
this._history = [];
}
}
const Wand = new Proxy(WandClass, {
construct(target, args) {
return new Proxy(new target(...args), {
get(target, prop, receiver) {
const value = target[prop];
const history = target._history;
if (typeof value == 'function') {
return (...args) => {
history.unshift(prop);
if (history.length > 3) history.pop();
return Reflect.apply(value, target, args);
};
}
else if (['prioriIncantatem', 'deletrius'].includes(prop)) {
return () => {
switch (prop) {
case 'prioriIncantatem':
console.log(history);
history.unshift(prop);
if (history.length > 3) history.pop();
break;
case 'deletrius':
history.length = 0;
history.unshift(prop);
break;
}
};
}
else { return Reflect.get(target, prop); }
}
});
}
});
Тем не менее, при попытке создания следующего экземпляра код не работает должным образом:
const w = new Wand({
alohomora: function () { console.log('unlocked!'); },
expelliarmus: function () { console.log('disarmed!'); }
});
w.unlockThenDisarm = function () {
this.alohomora();
this.expelliarmus();
};
w.unlockThenDisarm(); // EXPECTED: logs 'unlocked!' then 'disarmed!', NO ISSUE HERE
w.prioriIncantatem(); // EXPECTED: => ['expelliarmus', 'alohomora', 'unlockThenDisarm']
// instead got ['unlockThenDisarm']
Первоначально я думал, что существует проблема с привязкой this
, но так как console.log работает должным образом, Я предположил, что это не так. Из того, что я понимаю, не сам вызов - это то, что записывает в поле истории, а действие получения свойства. Не могу понять, как перехватить сами вызовы и добавить логи c регистрации вызовов при применении ловушки вместо получения ... Я попытался добавить ловушку напрямую, вот так
get(target, prop, receiver){
// ...
},
apply(target, thisArg, args) { debugger; }
, но отладчик никогда не запускается ...