JS: this reference does not work [duplicate] - javascript

This question already has answers here:
Using this in event handler in strict javascript?
(3 answers)
Closed 7 years ago.
I want to encapsulate my Javascript code for certain objects in structures as indicated below. However, I ran into trouble with the semantics of this.
While this during tiles.init() refers to the tiles object, in the event handlers it refers to the event, i.e. I cannot use this to call other methods from my object.
Is there any way to pass the object to the event handlers, such that I do not have to use the global variable to call my own sub-routines and still retain this from the callback context?
I put up a JSFiddle here. This is the working JS part:
myData = {
'color': "red",
'makeRed': function(){
// don't want reference to global here!
$(this).css("color",myData.color);
},
'reset': function(){
$(this).css("color","");
},
'init': function(){
$("#click").hover(this.makeRed,this.reset);
}
};
myData.init();
I found several solutions like this and this idea to pass additional arguments. The question has been marked a duplicate of this, but using .bind() wastes the this required for the jQuery inside the callback.
Any idea how to get both tiles and this of the event context to the handler function without using globals?

You can use a closure variable in the init method
'init': function () {
var self = this;
$("#tiles div.tile").hover(function () {
self.hoverIn(this);
}, function () {
self.hoverOut(this);
});
}
Your construct is not working because this inside the hover callbacks does not refer to the tiles object

Related

"This" within es6 class method [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
For some reason I'm getting weird values for "this" in my es6 class...
'use strict';
class Clicker {
constructor(element) {
this.count = 0;
this.elem = element;
this.elem.addEventListener('click', this.click);
// logs Clicker { count:0, elem: button#thing} as expected
console.log(this);
}
click() {
// logs <button id="thing">...</button> as unexpected...
console.log(this);
this.count++;
}
}
var thing = document.getElementById('thing');
var instance = new Clicker(thing);
<button id="thing">Click me</button>
Question:
Why is the "this" inside of of the Clickers' click method referring to the dom node rather than ... itself?
More importantly, how do I refer to Clickers' count property from within its' click method if I can't use "this" to do it?
Why is the "this" inside of of the Clickers' click method referring to
the dom node rather than ... itself?
Because the specification for .addEventListener() is to set the this pointer to the DOM element that caught the event. That's how it is designed to work.
When passing a method as a callback where you want to override the value of this, you can use .bind() to force the desired value of this with it:
this.elem.addEventListener('click', this.click.bind(this));
Explanation:
All function calls in Javascript set a new value of this according to how the function is called. See this explanation for further info on that basic set of rules.
On top of that, when you do this:
this.elem.addEventListener('click', this.click);
You are just getting the this.click method and passing that method alone to addEventListener(). The value of this will be completely lost. It's as if you are doing this:
var m = this.click; // m here is just a reference to Clicker.prototype.click
this.elem.addEventListener('click', m);
On top of this, .addEventListener() is specifically built to set it's own value of this when it calls the callback (to point this at the element creating the event).
So, you can use .bind() as shown above to force the proper value of this to be in place when your method is called.
For reference, you may find this description of the six ways that this is set for a function call in Javascript to be useful.
Other Options
I find .bind() to be the clearest way of defining this, but you could also use either a local anonymous function:
var self = this;
this.elem.addEventListener('click', function() {
self.click();
});
or in ES6, an arrow function:
this.elem.addEventListener('click', () => this.click());
The arrow function will preserve the value of this for you automatically to avoid needing the self reference used in the prior example.

How to get member variable in event handler called from a prototype function in a JavaScript class? [duplicate]

This question already has answers here:
Preserving a reference to "this" in JavaScript prototype functions [duplicate]
(7 answers)
Closed 6 years ago.
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'
};
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.
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.
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);
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)

Cached this, jQuery [duplicate]

This question already has answers here:
var self = this?
(8 answers)
What is the rationale behind the "var self = this" approach? [duplicate]
(4 answers)
Closed 9 years ago.
I often see the code like this one:
$('.element').on('click', function(event) {
var element = this;
// somecode that uses "element"
});
Is there any reason to cache this?
This is necessary if the code contains a closure that needs to access this. this is not a local variable, so it will not be captured in the closure, you need to bind a local variable for that purpose.
You also need it if the code contains a loop using a function like $.each(), and the body of the loop needs to refer to the element. These functions rebind this in the body to the current iteration object.
A lot of people set a reference to this if they need to reference it in another scope of code. For example:
$('.element').on('click', function(event) {
var element = this;
// somecode that users "element"
function foo() {
//$(this).text() WONT Work!
alert($(element).text()); //references the element that was clicked
}
});
Once you are inside a function or loop, this might refer to an object within that function. Therefor explicity assigning the element allows you to always access it, independent of the scope.
this is not a jQuery element, wrap it in $(this).
Caching is good because it stores the element, and it doesn't take up memory or processing time trying to re-find the element. However, this changes on scope so you might not want to cache that one.

Javascript OOP: binding method to event handler [duplicate]

This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 9 years ago.
I'm new to JS and I understand functions are the way to declare "class like" structures.
I'm trying to do something like this:
function Game() {
// Class stuff
this.handleClick = function(e) {
alert(e);
}
// Bind event to method previously defined
$('#board').bind('click', function(e) {
this.handleClick(e); // <--- THIS IS THE PROBLEMATIC LINE
});
}
Now, if in the "problematic line" I write:
handleClick(e)
I get Uncaught ReferenceError: handleClick is not defined.
Instead, if I write:
this.handleClick(e);
I get Uncaught TypeError: Object #<HTMLCanvasElement> has no method 'handleClick'
But then, if I do:
function Game() {
// Class stuff
this.handleClick = function(e) {
alert(e);
}
var game = this; // <--- I ASSIGN this TO board
// Bind event to method previously defined
$('#board').bind('click', function(e) {
game.handleClick(e); // <--- THIS WORKS!! WHY????
});
}
It works!?!?!
My questions are:
Why does this happen this way? I know this can be problematic, but why assigning it to a variable changes it like that?
Am I doing this wrong? Is there a better way to achieve something like this in a more elegant way?
You found the one hole in Javascript's implementation of closures. (If you don't know what closures are, don't worry about it.)
this in Javascript references the object on which the function was called. So if you have an object with a property that happens to be assigned the value of a function, and you call that property, the this pointer references the object. It does not reference whatever this was where the function was created, nor does it reference the function itself.
var object = {};
object.myfunc = function() { console.log(this); } //<-- this will refer to object
object.myfunc();
In the case of an event handler, the handler function (in pure Javascript, ignoring jQuery) gets assigned to the DOM element, so it gets called from that element and hence this points to the DOM element.
Using a local variable, like game in your example above, means that the function you created (a "closure") will "close" around that variable, capturing the values of any variable in scope at the place the function was defined. Any variable except this. So your work-around is a very common way to handle this.
Another common way to handle this would be to use a library like Underscore.js which has a function called bind that does some higher-order-function magic to make sure this always points to what you want.
$('board').bind('click', _.bind(function(e) {
this.handleClick(e);
}, this));
For this reason, Underscore.js is one of my favorite Javascript libraries (after jQuery), and I use it almost exclusively for the bind function.

Calling class methods within jQuery function

So I have some javascript class and in one method I use jQuery to bind function to click event. And within this function I need to call other methods of this class. In usual js function I did it through "this.method_name()", but here, I guess, jQuery redefines "this" pointer.
jQuery doesn't redefine the this pointer, but that's how JavaScript functions work in general. Store a reference to the this pointer under a different name, and use that.
var self = this;
$("selector").click(function() {
self.method_name();
});
See this answer for more approaches.
There are a few different ways to do this.
Anurag has a perfect example of one.
Two other ways are the jQuery Proxy class (Mentioned in other answers) and the 'apply' function
Now lets create an object with click events:
var MyObj = function(){
this.property1 = "StringProp";
// jQuery Proxy Function
$(".selector").click($.proxy(function(){
//Will alert "StringProp"
alert(this.property1);
// set the 'this' object in the function to the MyObj instance
},this));
//Apply Function
//args are optional
this.clickFunction = function(arg1){
alert(this.property1);
};
$(".selector").click(this.clickFunction.apply(this,"this is optional"));
};
In addition to the possibility of temporarily storing a reference to this (self = this, see Anurag's answer), since ES6 it is possible to use arrow functions for this problem. These have no "own" this.
This means that the "usual" object-related this can be accessed again within an arrow function within an event handler:
$("selector").click(() => {
this.method_name();
});
Further information:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions?retiredLocale=de#cannot_be_used_as_methods
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions?retiredLocale=de#using_call_bind_and_apply

Categories

Resources