我想在javascript类中的函数上插入pre execute和post execute钩子。
让我们说我有一个这样的课程。
class Foo {
method1(p1, p2) {
this.p1 = p1;
this.p2 = p2;
}
method2(p3) {
this.p3 = p3;
}
}
我想为这些预先存在的类方法定义一个before和after钩子。像这样的东西。
class Foo {
before(funName, ...params){
// Should print ('method1', [p1, p2]) when method 1 is called
// and ('method2', [p3]) when method 2 is called
console.log(funName, params)
}
after(funName, result){
// Should print the function name followed by its result
console.log(funName, result)
}
method1(p1, p2) {
this.p1 = p1;
this.p2 = p2;
}
method2(p3) {
this.p3 = p3;
}
}
export default Foo;
在现有代码中进行最少更改的情况下,实现这些挂钩的最佳方法是什么?
答案 0 :(得分:1)
以下是该问题的粗略解决方案:
// we iterate over all method names
Object.getOwnPropertyNames(Foo.prototype).forEach((name) => {
// First to do: we save the original method. Adding it to prototype
// is a good idea, we keep 'method1' as '_method1' and so on
Foo.prototype['_' + name] = Foo.prototype[name];
// Next, we replace the original method with one that does the logging
// before and after method execution.
Foo.prototype[name] = function() {
// all arguments that the method receives are in the 'arguments' object
console.log(`Method call: method1(${Object.values(arguments).join(', ')})`);
// now we call the original method, _method1, on this with all arguments we received
// this is probably the most confusing line of code here ;)
// (I never user this['method'] before - but it works)
const result = this['_' + name](...arguments);
// here is the post-execution logging
console.log(`Method result: ${result}`);
// and we need to return the original result of the method
return result;
};
});
请注意,此代码不是类本身的一部分,请按常规脚本执行。
这种简短的概念证明很有可能在现实世界的类上崩溃,并且需要一些额外的检查和特殊情况的处理程序,尤其是为了获得正确的日志输出。但这与您的Foo类一起使用。
答案 1 :(得分:0)
class Foo {
classAlias = false;
proxyHandle = {
// little hack where we save refrenece to our class inside the object
main : this,
/**
* The apply will be fired each time the function is called
* @param target Called function
* @param scope Scope from where function was called
* @param args Arguments passed to function
* @return Results of the function
*/
apply : function (target, scope, args) {
const func_name = target.name;
console.log('before', func_name);
// let's call some method of this class to actually check if this is the right instance
// also remember that you have to exclude methods which you are gonna use
// inside here to avoid “too much recursion” error
this.main._free.instanceCheck();
// here we bind method with our class by accessing reference to instance
const results = target.bind(this.main)(...args);
console.log('after', func_name, results);
return results;
}
}
constructor(classAlias) {
// Get all methods of choosen class
let methods = Object.getOwnPropertyNames( Foo.prototype );
// Find and remove constructor as we don't need Proxy on it
let consIndex = methods.indexOf('constructor');
if ( consIndex > -1 ) methods.splice(consIndex, 1);
// Replace all methods with Proxy methods
methods.forEach( methodName => {
this[methodName] = new Proxy( this[methodName], this.proxyHandle );
});
this.classAlias = classAlias;
}
// easies trick to do to avoid infinite loop inside apply is to set your functions
// inside object, as getOwnPropertyNames from prototype will only get methods
_free = {
instanceCheck : () => {
// this will check if this is our Foo class
console.log(this.classAlias);
}
}
log() {
return 'Result';
}
}
(new Foo('Proxy Class A')).log();
/*
Output:
before log
Proxy Class A
after log Result
*/
只是想分享,因为我在评论中看到有人在设置代理时遇到问题。您可以阅读更多关于代理的信息 here,以及here 更多关于申请的信息。
请记住,在代理句柄中,this
实际上是 this.main
。为了更好地理解,您可以将其更改为 classInstance
或类似的内容。