0

I'm new to JS, so I have this newbie question about right way to bind references. For example, I have this line of code in TypeScript:

  this.socket.on('new_task').subscribe(this.newTask);
...
  newTask(data) {
    this.logEvent('new_task', data);
    this.audio.playNewJob();
  }
  logEvent(event: string, data) {
    console.log(this.TAG + event + ' triggered with received data: ' + JSON.stringify(data));
  }

If I will try run this, I will get:

TypeError: this.logEvent is not a function

If I will change to:

this.socket.on('new_task').subscribe((data) => this.newTask(data));

Everything will work fine but it looks like a bad way to use JS and TS features. What is a recommended practice in this case?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Autumn_Cat
  • 790
  • 1
  • 14
  • 28
  • 1
    Possible duplicate of [How do JavaScript closures work?](https://stackoverflow.com/questions/111102/how-do-javascript-closures-work) – Hampus May 31 '17 at 12:25
  • When you pass `this.newTask` as a callback reference, the association between the function and `this` is lost; in fact there really never is such an association. – Pointy May 31 '17 at 12:26
  • Have you tried using arrow-function? `(data => this.newTask(data))` – Arg0n May 31 '17 at 12:26
  • @Arg0n it's at the end of the question. – Pointy May 31 '17 at 12:26
  • @Pointy I don't see the problem in using that. – Arg0n May 31 '17 at 12:28
  • Neither do I :) – Pointy May 31 '17 at 12:28
  • 1
    I would say this is better than using `bind` since you will lose the type checking for the callback function if you use `bind`. See [this issue](https://github.com/Microsoft/TypeScript/issues/212) – Saravana May 31 '17 at 12:33

3 Answers3

2

change it to

.subscribe(this.newTask.bind(this))
UXDart
  • 2,500
  • 14
  • 12
2

The issue is how "this" is bound, it's done at execution time so when the logEvent function is invoked this refers to the global object which doesn't have that function. Your second example is fine, in that case this is bound when the arrow function is defined, an alternative is to store a references to this and call the function from that but personally I prefer the arrow function approach. Beware that not everyone agrees with this but I'm used to arrow functions from c# and I think if you understand the differences they read easier as well as having a simpler way of reasoning about this.

Matt
  • 12,569
  • 4
  • 44
  • 42
0

I'd face to this issue with property, instead of method. I.e. we can declare kind of same functionality with "property syntax" .. where property is a function (newTask below is method, newTaskAsProperty is more property-ish)

class MyClass {
    newTask(data) {
        this.logEvent('new_task', data);
        this.audio.playNewJob();
    }    
    newTaskAsProperty = (data) =>{
        this.logEvent('new_task', data);
        this.audio.playNewJob();
    }
    ...

what is interesting, is what would be the transpiled result:

var MyClass = (function () {
    function MyClass() {
        var _this = this;
        this.newTaskAsProperty = function (data) {
            _this.logEvent('new_task', data);
            _this.audio.playNewJob();
        };
    }
    MyClass.prototype.newTask = function (data) {
        this.logEvent('new_task', data);
        this.audio.playNewJob();
    };

So, the property approach (newTaskAsProperty = (data) =>{}) is applied in constructor, to the instance, not to prototype. And that is, why using is as a delegate will work as expected

// not working
// this.socket.on('new_task').subscribe(this.newTask);

// working
this.socket.on('new_task').subscribe(this.newTaskAsProperty);

The only downside is - it is not a method, i.e. cannot be overriden.

As a workaround, we can simply do call a real method inside of that - if needed, and that method would have this properly set and could be overriden...

Radim Köhler
  • 122,561
  • 47
  • 239
  • 335