3

In the code below, is there a better way to reference the object instance from handleClick() than pulling it in as a global?

var Widget = function() {
  this.property = 'value';
  this.bindEvents();
}

Widget.prototype = {

  bindEvents: function() {
    $('button').on('click', this.handleClick);
  },

  handleClick: function() {
    var self = window.Widget;

    console.log(self.property);
  }
}

window.Widget = new Widget();

This question asks the same thing, and the (non-accepted) answer is to pass a callback to $.on() and call the handler from there, passing in the instance as a parameter like this:

bindEvents: function() {
  var self = this;

  $('button').on('click', function() {
    self.handleClick.apply(self);
  });
}

Is this technique of passing the instance around really the right way to do it, or is there still a preferred way besides the two I've shown?

Community
  • 1
  • 1
cantera
  • 24,479
  • 25
  • 95
  • 138
  • 1
    Look at all those questions: http://stackoverflow.com/q/18210083/218196, http://stackoverflow.com/q/5904050/218196, http://stackoverflow.com/q/737454/218196, http://stackoverflow.com/q/520019/218196. – Felix Kling Sep 03 '13 at 19:43
  • Thanks for the dup links - likely wouldn't have posted my question if I knew to search for `$.proxy`. Will leave it up though unless there are objections - some good answers and might help someone to see multiple techniques referenced in the code. – cantera Sep 03 '13 at 19:56
  • FWIW, this was the search I used: http://stackoverflow.com/search?q=%5Bjquery%5D+oop+event+handler+context – Felix Kling Sep 03 '13 at 20:51

5 Answers5

5

The object can be passed as eventData to on(), like so:

var Widget = function () {
    this.property = 'value';
    this.bindEvents();
}

Widget.prototype = {

    bindEvents: function () {
        $('button').on('click', {o: this}, this.handleClick);
    },

    handleClick: function (e) {
        var widgt = e.data.o;
        console.log(widgt.property);
    }
}

window.Widget = new Widget();

FIDDLE

This keeps the scope of the event handler as it should be.

adeneo
  • 312,895
  • 29
  • 395
  • 388
  • Very interesting approach. Helps me realize that calling the handler within a callback has the advantage of not having to pass the event object around. – cantera Sep 03 '13 at 19:47
  • @cantera25 - indeed, and `this` would still be the clicked button inside the event handler, and not the `Widget` object. – adeneo Sep 03 '13 at 19:49
  • Another advantage of this method is that you can easily unbind the event listener later with `$('button').off('click', this.handleClick);` – rassoh Mar 20 '19 at 22:35
1

You could use bind():

bindEvents: function() {
  $('button').on('click', function() {
    this.handleClick();
  }.bind(this)); 
}

But when it comes to IE, that would work only in IE >= 9.

Edit: in legacy IE, you could use, as suggested by @Kevin B in the comment, jQuery's $.proxy():

bindEvents: function() {
  $('button').on('click', $.proxy(function() {
    this.handleClick();
  }, this)); 
}
kamituel
  • 34,606
  • 6
  • 81
  • 98
0

As @kamituel says, bind can be used:

Widget.prototype = {
  bindEvents: function() {
    $('button').on('click', this.handleClick.bind(this));
  },
  handleClick: function() {
    console.log(this.property);
  }
}
Paul Grime
  • 14,970
  • 4
  • 36
  • 58
0

The specified technique is called closure, and it is in common use. However, sometimes it is important to pay attention not to cause memory leaks. You can read more about closure here:

How do JavaScript closures work?

and about memory leaks related to closures, here:

Memory leak risk in JavaScript closures

Community
  • 1
  • 1
Ilan
  • 624
  • 6
  • 11
0

The best way to preserve this are lambda functions:

$('button').on('click', () => this.handleClick());

or if you would like to keep the reference to the event:

$('button').on('click', (event) => this.handleClick(event));

The special thing about lambda functions is that you don't define another function in a (potentially different) context. The lambda term is just an instruction and therefore part of the object it was defined in - in your case the instance created by window.Widget = new Widget().

Corbie
  • 819
  • 9
  • 31