0

I am looking at the implementation of once() in event.js:

EventEmitter.prototype.once = function(type, listener) {
  if ('function' !== typeof listener) {
    throw new Error('.once only takes instances of Function');
  }

  var self = this;
  function g() {
    self.removeListener(type, g);
    listener.apply(this, arguments);
  };

  g.listener = listener;
  self.on(type, g);

  return this;
};

Remembering that you would do some_object.once('on', function(){ console.log(this); } );

The temporary function g() has self.removeListener(type,g). I assume that's because the context in g() would be wrong otherwise. But then, the line after that, the variable this is used in listener.apply(this, arguments); But... isn't this then passing the global context to listener(arguments)?

Then a little down, it runs self.on instead if this.on.

Now... I am confused. I am fairly new to Javascript, and I still get confused with the context. But, this code is driving me bananas... can somebody please enlighten me here?

Thanks,

Merc.

Merc
  • 16,277
  • 18
  • 79
  • 122

1 Answers1

1

The reason to use a copy of this like self in your example is when this won't be set like you want in a given function call, you can reference a saved copy in self and always have access to it.

In Javascript, this is set by the calling context unless set explicitly with .call() or .apply().

A normal function call will cause this to be set to the global object.

A method call like x.once() will cause this in the once() method to be set to the object x.

In your particular case, g() is being used as a callback function. As such, this will be set by the context used in the caller of that callback function. But, if that context isn't as desired and g() wants to have access to the EventEmitter object, then it can use the self reference that has been saved in the closure that g() resides in. Without the saved reference in self the function g() might not have access to the desired value because this inside the g() function is likely set to something else.

If I follow the intent of this .once() method, then I see no reason for the self reference because .on() will cause this to be set as needed when it calls g(). In that case, it may just be a convention that is being followed that works safely when callbacks are used that do not have this set as desired (even though that isn't strictly the case in this particular code).

As for calling self.on() instead of this.on(), there is no difference in execution. The value of this does not change within a given function context. There are some libraries that assign this to a local variable and then reference the local variable primarily because it can save a few bytes upon minimization because the local variable can be renamed by the minimizer to a single byte value, but the value of this cannot be renamed.

Here's a related post with a longer discussion: What is the value of var me = this;

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • About "Without the saved reference in self the function g() might not have access to the desired value because this inside the g() function is likely set to something else." ... I am not sure about this one though. Reading the code of events.js, `EventEmitter.prototype.emit()` has `... listeners[i].apply(this, args);` . So that `this` in the caller is still the same as the object...? I just don't see the point of `that` in the function at all... and there must be one? [events.js](https://github.com/joyent/node/blob/master/lib/events.js) see lines 114 (calling) and 184 (`once()`) – Merc May 20 '12 at 05:34
  • @Merc - I say the same thing in the paragraph that starts `If I follow...`. I think `self` is just being done for convention or minimization reasons here, not because it needs to be done to make the code work. – jfriend00 May 20 '12 at 05:49
  • You did say the same thing...! OK, so I am not going insane. Thank you! (answer accepted) – Merc May 20 '12 at 05:58