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?

3

5 Answers 5

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.

Sign up to request clarification or add additional context in comments.

3 Comments

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.
@cantera25 - indeed, and this would still be the clicked button inside the event handler, and not the Widget object.
Another advantage of this method is that you can easily unbind the event listener later with $('button').off('click', this.handleClick);
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)); 
}

8 Comments

In IE, you can use jQuery's version of .bind: $.proxy()
@kamituel - thanks, is there any advantage to using .bind() vs. the second approach I showed in my question?
@cantera25: Besides being more concise, no.
@FelixKling - wouldn't bind() be a little bit faster? (no reaching to the outer scope)
I'll answer myself: this SO answer explains why bind() tends to be slower.
|
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);
  }
}

Comments

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

Comments

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.