Reputation: 868
I have a es6 class with more then 100 functions.
I added a new function which I have to run each time any other function is called.
I found a lot of solutions but no one solved my problem...
I know I can add this call to each function but then I'll end with code repeat and a lot of work for all the functions.
That is that I've done so far:
return class Test {
constructor () {}
addedFN() {
console.log('FN');
}
A() {
this.addedFN();
console.log('A');
}
B() {
this.addedFN();
console.log('B');
}
C() {
this.addedFN();
console.log('C');
}
...
}
As we can see in this approach I have a lot of code duplication which I want to avoid...
Also, the solution to call the new function first and then inside to call the next function won't work, because all the functions are used in a lot of places in the application.
I tried also to run the following code in the constructor, but that is a bad solution as my class is called a lot of times and then this code will run each time:
for(const key of Object.getOwnPropertyNames(Test.prototype)) {
const old = Test.prototype[key];
Test.prototype[key] = function(...args) {
addedFN(...args);
old.call(this, ...args);
};
}
Upvotes: 3
Views: 897
Reputation: 29085
This might be a good use for a Proxy
to transparently change your object without actually changing the object, just wrapping it in a proxy.
A get
trap can check if you are accessing a method and then dynamically wrap it into a proxy with an apply
trap.
class Test {
A() { console.log('A'); }
B() { console.log('B'); }
C() { console.log('C'); }
get X() { return 1; }
Y = 2;
}
function addedFN() { console.log('FN'); }
const loggingHandler = {
apply() {
addedFN();
return Reflect.apply(...arguments);
}
};
const wrapMethods = {
get() {
const result = Reflect.get(...arguments);
if (typeof result === "function")
return new Proxy(result, loggingHandler);
return result;
}
};
const test = new Test();
const foo = new Proxy(test, wrapMethods);
foo.A();
foo.B();
foo.C();
console.log(foo.X);
console.log(foo.Y);
.as-console-wrapper { max-height: 100% !important; }
The approach can be generalised to accept different possible modifiers to methods and cache them in a WeakMap
class Test {
A() { console.log('A'); }
B() { console.log('B'); }
C() { console.log('C'); }
get X() { return 1; }
Y = 2;
}
function addedFN() { console.log('FN'); }
const noop = () => {};
const wrapFunction = ({before = noop, after = noop}) => ({
apply() {
before();
const result = Reflect.apply(...arguments);
after();
return result;
}
});
const wrapMethods = handler => ({
_cache: new WeakMap(),
get() {
const result = Reflect.get(...arguments);
if (typeof result === "function") {
if (!this._cache.has(result))
this._cache.set(result, new Proxy(result, handler));
return this._cache.get(result);
}
return result;
}
});
const loggingHandler = wrapFunction({before: addedFN});
const test = new Test();
const foo = new Proxy(test, wrapMethods(loggingHandler));
foo.A();
foo.B();
foo.C();
console.log(foo.X);
console.log(foo.Y);
Upvotes: 3
Reputation: 6264
Don't run that in the constructor, run that after the class declaration
Also, that solution will result in too much recursion, since it will also apply to addedFn
, which will call addedFn
, which will call addedFn
... etc, until your browser throws a "too much recursion" error
So you also don't want to apply it to the addedFn
In the code below, I also don't apply it to constructor
... however, even when I do, it doesn't call addedFn
when the object is constructed, so, to make it more clear, I explicitly don't touch the constructor
class Test {
constructor(y) {
this.y = y
}
fun1(x) {
console.log('I am fun1', x, this.y);
}
fun2() {
console.log('I am fun2');
}
addedFn(...x) {
console.log('added function', ...x);
}
}
Object.getOwnPropertyNames(Test.prototype).forEach(f => {
if (f !== 'constructor' && f !== 'addedFn' && typeof Test.prototype[f] === 'function') {
const old = Test.prototype[f];
Test.prototype[f] = function(...args) {
this.addedFn(...args);
return old.apply(this, args);
}
}
})
let c = new Test('works');
c.fun1(1,2,3)
Note: you should also test if Test.prototype[f]
is a function - unless your class ONLY has functions
Upvotes: 4