3

I am starting to get my feet wet using the publish/subscribe pattern in JavaScript, but now facing a problem I don't understand.

Take the following scenario:

On button click, a message is emitted, this works.

document.getElementById('myBtn').addEventListener('click', function() {
    PubSub.publish('topic', data);
});

In an other part of my application, I pick up this message:

function MyObj() {
    this.eventLog = [];
    var token = PubSub.subscribe('topic', this.logger);
};

MyObj.prototype.logger = function(message, data) {
    this.eventLog.push(data);
}

Here I want to store the published data in the eventLog property of the MyObj object. Unfortunately, this isn't working:

Uncaught TypeError: Cannot read property 'eventLog' of undefined

So it seems like the context of this is lost – when I do console.log(this), the window object is logged.

I know this can be tricky for beginners, but until now I was always able to understand what is happening, but this leaves me completely puzzled. Of course MyObj gets initialized before the message is published, so I don't see any problem here. Could somebody explain to me please what is happening here?

Sven
  • 12,997
  • 27
  • 90
  • 148
  • 3
    The binding of `this` has to do with the way a function is **called**. When you pass `this.logger` as a callback parameter, all that the called function sees is the reference to the function, and the original relationship to `this` is completely lost at that point. Use `.bind()` to create a wrapper function to fix the value of `this`. – Pointy May 26 '15 at 21:05
  • @Pointy Thank you for explaining what is happening – I unterstand it now. Also I finally managed to understand how to use `bind()`, really exiting stuff! – Sven May 26 '15 at 21:19

2 Answers2

5

Use Function.prototype.bind:

 PubSub.subscribe('topic', this.logger.bind(this))
David Hellsing
  • 106,495
  • 44
  • 176
  • 212
0

To clarify/explain the observed behaviour - JavaScript uses lexical scoping, so the this keyword is bound to the most inner scope of the usage. Depending where (in which function and parent of the function and/or object) the this keyword is used, it is a different object.

In your case when calling the this.eventLog in your MyObj this is referring to the MyObj. But in the part

MyObj.prototype.logger = function(message, data) {
    this.eventLog.push(data);
}

the this keyword is called in the scope of the window object (parent is the window object) and that is why you see it when printed instead of MyObj.

Technically the context of this is always pushed/saved in a stack, so the context is not lost, but the current context is popped/retrieved from the stack regarding the current scope.

This behaviour is very well (also visually) described in this article Quirksmode - The this keyword. A few good examples can also be found in this SO post.

Community
  • 1
  • 1
keenthinker
  • 7,645
  • 2
  • 35
  • 45