1

I was following the node functional programing tutorial in here, but when I tried implement my code for lesson # 12 as follow

function Spy(target, method) {
  var store={};
  var self=this;
  var copy=target[method];
  store[target[method]]=0;
  console.log(store);
  console.log(store[target[method]]);
    target[method]=function(){
        store[target[method]]+=1;
      return copy.apply(this,arguments);
    };
  
  return {count:store[target[method]]}; 
}

var spy = Spy(console, 'error');

console.error('calling console.error');
console.error('calling console.error');
console.error('calling console.error');

console.log(spy.count);

I got the console.log(store) within Spy return an object containing a function. Also, the final return return {count:store[target[method]]}; from Spy return undefined. Can anyone please explain the reasons behind these two? Thanks

Community
  • 1
  • 1
seanh
  • 77
  • 4
  • 8

2 Answers2

1

Because after setting store[target[method]]=0; you are setting target[method] to be equal to a function, this messes up the value of store[target[method]] and makes it undefined. Looks like you will want to use your copy value on the return:

return {count:store[copy]}; 

Although that doesn't help that the count will still be 0 in this case. This is because you are returning an object in Spy directly {prop: value, ...}, as such you cannot really modify it within the Spy function. Although to get around that define the object {count:store[copy]} as a variable within your constructor (var returnObj = {count:store[copy]};), then return that variable: return returnObj. Now you can update returnObj.count inside [target[method]].

This works because Object's in JavaScript are passed-by-reference.

function Spy(target, method) {
  var store={};
  var self=this;
  var copy=target[method];
  store[target[method]]=0;
  var returnObj = {count:store[copy]};

    target[method]=function(){
        returnObj.count+=1;
        return copy.apply(this,arguments);
    };

  return returnObj; 
}

var spy = Spy(console, 'error');

console.error('calling console.error');
console.error('calling console.error');
console.error('calling console.error');

console.log(spy.count);
Spencer Wieczorek
  • 21,229
  • 7
  • 44
  • 54
  • Thank you very much for the explanation and the solution :} – seanh Sep 23 '15 at 14:57
  • Just want to add a follow up, after I read http://stackoverflow.com/questions/9437981/why-isnt-this-object-being-passed-by-reference-when-assigning-something-else-to is it truth such that passing object in JS should be more like a pass by pointer scenario in C++ than passing reference in C++, since the pointer can be override? – seanh Sep 23 '15 at 16:38
  • @seanh I would still consider it pass-by-reference since that's how it's functionality working, but rather implemented slightly different. It's passed by [*"copy of a reference"*](http://stackoverflow.com/questions/13104494/does-javascript-pass-by-reference). Basically it passes by value of the reference, so it's implemented as pass-by-value but functionality acts as if it's pass-by-reference. [The value being passed in this case is the reference itself](http://stackoverflow.com/questions/518000/is-javascript-a-pass-by-reference-or-pass-by-value-language). – Spencer Wieczorek Sep 23 '15 at 16:44
  • Thank you very much for your input, I am really appreciate you help :) – seanh Sep 23 '15 at 16:50
0

I would like to explain the stuff with the additional logs i have put in your code

function Spy(target, method) {
  var store={};
  var self=this;
  var copy=target[method];  // #0.1
  store[target[method]]=0; //  #0.2
  console.log('Before :: store', store); // #1
  console.log('Before :: target[method]', target[method]); // #2

  target[method]=function(){
      store[target[method]]+=1;
      return copy.apply(this,arguments);
  }; // #3

  console.log('After :: store', store);  // #4
  console.log('After :: target[method]', target[method]);  // #5
  return {count:store[target[method]]}; // #6
}

var spy = Spy(console, 'error');
console.log(spy.count);

target[method] is a function which looks something like function () { [native code] } (replace native code with some code written in js lib).

At 0.2 you are making this function (in stringified form) a key of store object and assigning its value to 0. So, your store object looks something like

`Before :: store { 'function () { [native code] }': 0 }`

at #1 and target[method] is a native function at #2.

Now at #3 you are assigning target[method] a new function, so now onwards target[method] will refer to your new function.

So, at #4 your store objects remains same. (Since key is a stringified value of function.) which is

`After :: store { 'function () { [native code] }': 0 }`

but since you made assignment in #3, so your target[method] value has changed to new function which is

After :: target[method] function (){
      store[target[method]]+=1;
      return copy.apply(this,arguments);
  }

Now at #6 you are trying to fetch a key which from store object which does not exists in store object, so that is why it returns undefined and store value of count to undefined

Gaurav Gupta
  • 4,586
  • 4
  • 39
  • 72
  • Thank you very much, this totally makes sense now :) How about the native code comes from, it doesn't look like it is something that pass in by the caller of Spy ? – seanh Sep 23 '15 at 14:34
  • native code comes from javascript engine. Your code runs on top of Js Engine which implements ECMA specs. In this implementation their is an object called `console` which has `error` function. This error function has some implementation in it as well, and `in our logs native code` refers to `console.error()`'s implementation. – Gaurav Gupta Sep 24 '15 at 05:53