首页 > 解决方案 > JavaScript: TypeError: work.calls is not iterable in function decorator (beginner question)

问题描述

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?

标签: javascriptdecoratortypeerror

解决方案


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 __;
}

推荐阅读