20

What is the correct way to preserve a this javascript reference in an event handler stored inside the object's prototype? I'd like to stay away from creating temp vars like '_this' or 'that' and I can't use a framework like jQuery. I saw a lot of people talk about using a 'bind' function but was unsure of how to implement it in my given scenario.

var Example = function(foo,bar){
    this.foo = foo;
    this.bar = bar;
};
Example.prototype.SetEvent = function(){
    this.bar.onclick = this.ClickEvent;
};
Example.prototype.ClickEvent = function(){
    console.log(this.foo); // logs undefined because 'this' is really 'this.bar'
};
0

4 Answers 4

23

I find bind() being the cleanest solution so far:

this.bar.onclick = this.ClickEvent.bind(this);

BTW the other this is called that by convention very often.

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

7 Comments

I hate creating a reference to this called "that" (a la Crockford). I prefer "instance" (a la Stefanov), or "me" (a la ExtJS)
Actually, I hate creating this references, no matter how are they named. But agree, that isn't the best naming.
I use 'self', but found just recently that is a name clash with a top-level property in WebWorkers. Not that you can't have the same name, but it does confuse things. Also, I find the name 'that' to be quite the opposite to 'this'
@GeorgeJempty that is just a personal preference
this of course doesn't work in old IE (8 and older)
|
7

Check out the MDN document on bind: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind

Using this functionality, you can change the scope (what this is):

Example.prototype.SetEvent = function(){
    this.bar.onclick = this.ClickEvent.bind(this);
};

Be aware, however, that this is a new addition to EMCA and thus may not be supported in all user agents. There is a pollyfill available at the MDN document linked above.

Comments

5

The problem with bind is that is only supported by IE9+.

The function can be polyfilled with es5-shim, but it's not completely identical to the native implementation:

  • Caveat: the bound function has a prototype property.
  • Caveat: bound functions do not try too hard to keep you from manipulating their arguments and caller properties.
  • Caveat: bound functions don't have checks in call and apply to avoid executing as a constructor.

Another alternative can be jQuery.proxy:

$(elem).on('click', $.proxy(eventHandler, this));

This is even more helpful if you want to remove the event handler later, because when a function goes through the proxy method, jQuery generates a new guid value and then applies that guid to both the core function as well as the resultant proxy function, so that you can use the original function reference to unbind an event handler callback that has been proxied:

$(elem).off('click', eventHandler);

2 Comments

.bind() is no less fully cross-browser than $.proxy. It's basically a non-standard version of .bind(). If the caveats you listed are actually a concern, then $.proxy won't be any better. Not to mention that the question is clear that he can't use jQuery.
@squint I'm suggesting to use $.proxy (or a similar implementation) because it's a loose connection, and that's something really helpful in OO event handling. But thank you I'll update my answer because, like you're saying, a polyfilled .bind() is no less fully cross-browser than $.proxy.
0

Other solution: use the "arrow functions" introduced by ES6. Those have the particularity to not change the context, IE what this points to. Here is an example:

function Foo(){
    myeventemitter.addEventListener("mousedown", (()=>{
        return (event)=>{this.myinstancefunction(event)}; /* Return the arrow
function (with the same this) that pass the event to the Foo prototype handler */
    })());
}
Foo.prototype.myinstancefunction = function(event){
    // Handle event in the context of your Object
}

Arrow function specs @ MDN

Edit

Be carefull with it. If you use it client-side and you can't be sure of the capabilities of the JS interpreter, note that old browser won't recognize arrow functions (see CanIUse stats). Use this method only if you KNOW what will run it (recent browsers only & NodeJS apps)

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.