Autumn_Cat
Autumn_Cat

Reputation: 800

TypeError: is not a function. Right way to bind this. references in JS

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?

Upvotes: 0

Views: 648

Answers (3)

Radim Köhler
Radim Köhler

Reputation: 123891

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...

Upvotes: 0

Matt
Matt

Reputation: 13379

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.

Upvotes: 2

UXDart
UXDart

Reputation: 2620

change it to

.subscribe(this.newTask.bind(this))

Upvotes: 2

Related Questions