Reputation: 37
I was trying to pass a function work
into a decorator where it will save function calls in an array work.calls
.
The function:
work(a, b) {
console.log(a + b);
}
The decorator:
function decorator(func) {
func.calls = [];
return function(...args) {
func.calls.push(args);
return func.call(this, ...args);
}
}
Decorate function work
:
work = decorator(work);
Calling new function:
work(1, 2); // 3
work(4, 5); // 9
for (let args of work.calls) {
console.log( 'call:' + args.join() ); // TypeError: work.calls is not iterable
}
work.calls
is an array so why is it not iterable?
For reference, there's another version of decorator written by someone else that actually works:
function decorator(func) {
function wrapper(...args) {
wrapper.calls.push(args);
return func.apply(this, arguments);
}
wrapper.calls = [];
return wrapper;
}
work(1, 2); // 3
work(4, 5); // 9
for (let args of work.calls) {
alert( 'call:' + args.join() ); // "call:1,2", "call:4,5"
}
What does wrapper
do here and why this way works?
Upvotes: 3
Views: 129
Reputation: 144689
The 2 functions do not work the same. Your decorator defines the calls
property on the passed function. The other decorator function sets the property on the nested and returned (wrapper
) function. You get that error as work.calls
is undefined
. The decorated work
refers to the anonymous function: return function(...args)
which doesn't have calls
property.
If you change work = decorator(work);
to let work2 = decorator(work);
then you will see that calls
is set on the original work
function and not on work2
.
Your code works the same if you define the calls
property on the returned function of the closure.
function decorator(func) {
function __(a, b) {
__.calls.push([a, b])
return func(a, b)
}
__.calls = []
return __;
}
Upvotes: 3