0

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.

AZ ZA
  • 13
  • 2
  • 1
    the returned `wrap` function is not the original `work` function - so, the global `work` function is now the `wrap` function, and therefore does not have the `calls` property, since that exists on the original `work` function - try `test = work` before `work = spy(work)` you'll see that `test` will have the `calls` property you create on the original `work` – Bravo Jul 29 '21 at 22:04
  • "*does this mean function object properties are NOT persistent unless created from within their own body?*" - no. `wrap.calls = [];` does not create the property from inside of the body of `wrap` either. – Bergi Jul 29 '21 at 23:27
  • Thanks for the help. I think I was mixed up by what each function was returning. The ``` spy(work) ``` call was actually returning the nested wrap(x, y) function. And when I set ``` work = spy(work) ```, I was overwriting the original global work function object with the new 'nested wrap' function. – AZ ZA Jul 30 '21 at 22:23

1 Answers1

0

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.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you for your help. I think the problem is I didn't understand that spy(work) was returning wrap(x, y), I thought the entire chain of nested functions was being returned. As a side question, is it possible to invoke the entire ```spy( work(1, 2) )``` nested functions in one line? Or would I have to always put the ```let someVar = spy(work)``` in order to start using the nested ```wrap(x, y)``` function? I would upvote you but my rep is still too low – AZ ZA Jul 30 '21 at 22:26
  • You can in theory write `spy(work)(1,2)`, but then you have no variable with the wrapped function on which you can access `.calls`. If you insist on one line, `(spiedWork = spy(work))(1, 2)` is also possible, but I wouldn't advise it either. – Bergi Jul 30 '21 at 22:42
  • Thanks again for your quick responses. No, I don't insist on it, I am just learning and just wanted to know if it was possible in theory. I appreciate the help :) – AZ ZA Jul 30 '21 at 23:33