Search code examples
javascriptfunctionobjectnested-functionobject-properties

'Object property set inside decorator function doesn't persist as expected.'


I've only been learning to use JavaScript for 4 months, so please let me know if there is something obvious I am missing.

In this problem, I am writing 2 functions,

  • One is the inner (non-critical) work function. It just adds 2 numbers.
  • The second is the decorator function which needs to store the arguments of work function into the function object properties of work itself, as work.calls = [ [num1, num2], [etc, etc] ];
  • According to the setup of the problem, after being defined, the functions need to be called like this:
work = spy(work);
work(1, 2);
work.calls; // => [1, 2]

I followed the section 'Custom Properties' of this article Function Object. So I'd expect the function object property (work.calls) to persist in the work object after I invoked spy(work).

When I ran the code under the debugger, I got a strange result.

  • The work.calls is defined inside the block of the spy function
  • The work.calls becomes undefined at work(1, 2)

My attempted solution looks like this:

function work(num1, num2) {
  return num1 + num2;
}

function spy(target) {
  target.calls = [];
  //set work.calls, work.calls is currently existing in debugger.
  
  function wrap(x, y) {
   
    target.calls.push([x, y]);
    
    return target(x, y);
  };
  
  
  return wrap
}

work = spy(work);

work(1, 2);
work.calls; // => now undefined in the debugger!

Another solution I found suggest to change target.calls to wrap.calls, and it fixed the problem. So does this mean function object properties are NOT persistent unless created from within their own body?

function spy(target) {
  
  
  function wrap(x, y) {
   
    wrap.calls.push([x, y]);
    //using wrap instead of target allows work.calls to be persistent 
    
    return target(x, y);
  };
  
  wrap.calls = [];
  
  return wrap
}

work = spy(work);

work(1, 2);

work.calls; // => [1, 2] all good in work.calls... but why?

I can't figure out why setting wrap.calls allows the property to persist. Aren't objects always persistent on the global scale?

Thanks so much for your patience. I just hope someone can help me make sense of this.


Solution

  • I recommend to avoid work = spy(work); at first. Instead write

    const spiedWork = spy(work); …

    Then compare what happens when calling work(1,2) and what happens when calling spiedWork(1,2). Also compare spiedWork.calls with work.calls. Once you have this figured out (and that spiedWork !== work), you'll understand what happened in the original code.