Javascript callbacks losing 'this' - javascript

I have an issuer where I lose the this inside this object. The output of the following piece of JavaScript gives me "some-id" and then undefined. When I use this inside a callback function, the scope goes out of the object and it cannot use this any more. How can I get the callback to use 'this' or at least have access to the object?
Since I will make multiple objects, I won't be able to create a 'static' like storage.
Here is my test code that you can use to reproduce my problem. What I would like to have is CheckBox.doSomething() to return the value of this.id which should match some-id for this test case.
function CheckBox(input_id) {
this.id = input_id;
this.doSomething();
$('#some-element').click(this.doSomething);
}
Checkbox.prototype.doSomething = function() {
alert(this.input_id);
}
var some_box = new CheckBox('some-id');
some_box.doSomething();
$('#some-element').click();
I can't even get this to work as I want it to:
function CheckBox2(input_id) {
this.id = input_id;
alert(this.id);
}
CheckBox2.prototype.doSomething = function() {
alert(this.input_id);
}
var some_box = new CheckBox2('some-id');
some_box.doSomething();

Your problem is with this line: $('#some-element').click(this.doSomething);
Why this is a problem
JavaScript methods don't know anything about the object that should be assigned to this, it's set when the method is called either explicitly (with myFunction.call(obj)) or implicitly (when called using obj.myFunction()).
For example:
var x = {
logThis: function () {
console.log(this);
}
};
x.logThis(); // logs x
x.logThis.call(y); // logs y
var func = x.logThis;
func(); // logs window: the fallback for when no value is given for `this`
In your case, you're passing this.doSomething to jQuery, which is then explicitly calling it with the element that was clicked as the value of this. What's happening is (a slightly more complex version of) this:
var callback = this.doSomething;
callback.call(anElement, anEvent);
The solution
You need to make sure that doSomething is called with the right value of this. You can do that by wrapping it in another function:
var cb = this;
$('#some-element').click(function() {
return cb.doSomething();
});
jQuery provides a proxy function lets you do this more simply:
$('#some-element').click(jQuery.proxy(this.doSomething, this));

function CheckBox(input_id) {
this.id = input_id;
this.doSomething = $.proxy( this.doSomething, this );
$('#some-element').click(this.doSomething);
}
The "javascript equivalent" of this is Function#bind but that is not available in every browser and since it seems you are using jQuery I am using the jQuery equivalent $.proxy

Others have already explained the causes of the problem and how to fix it with jQuery. What's left is how you fix it with standard JavaScript. Instead of ...
$('#some-element').click(this.doSomething);
... you write:
document.getElementById('some-element').addEventListener('click', this.doSomething.bind(this));
This changes the context of this inside doSomething. You can also do that with anonymous functions - instead of ...
$('#some-element').click(function(event) {
console.log(this);
});
... you write:
document.getElementById('#some-element').addEventListener('click', (function(event) {
console.log(this);
}).bind(this));
That has been very useful to me in projects with lots of callbacks, e.g. in Node.js (where you don't have to care about outdated browsers).
Edit: getElementById() and addEventListener() instead of $(...).click(...).

Related

"this" in javascript objects with closures behaving inconsistently

So, this is probably answered somewhere on this site, but I can't find it, if it is.
I'm having trouble figuring out why one of my this references inside functions seems to be resolved when I create the object, and one when I call the function that has the reference inside it. Here's some code:
function MyObj (name) {
this.locked = false;
this.name = name;
this.elem = null;
this.func1 = function () {
if (this.locked) return;
/* code that changes this.name here */
this.elem.innerHTML = this.name;
};
this.func2 = function () {
this.locked = !this.locked;
if (this.locked) this.elem.className = "locked";
else this.elem.className = "unlocked";
};
}
var myObjGlobal = new MyObj("foo");
function callFunc1 () {
myObjGlobal.func1();
}
Then I have a function that is called on document load:
function onLoad() {
var myElem = document.getElementById("myElem");
myObjGlobal.elem = myElem;
myElem.onclick = myObjGlobal.func2;
document.getElementById("myButton").onclick = callFunc1;
}
I've made sure all my html elements have the right ids. When I click myButton, I get no errors. However, when I click myElem, I get Uncaught TypeError: Cannot set property 'className' of undefined.
Why is the first this set when I call the function, and the second this set when I create the object? (Or so it seems?)
here's a working jsfiddle showing the problem (with the given example code).
Thanks in advance!
myElem.onclick = myObjGlobal.func2;
This doesn't do what you think inn JavaScript. It doesn't give you func2 with the object "attached" to it in any way; it just gives you func2. When it gets called later, it's called as a method of myElem, so that's what this is.
This is a gigantic and awful wart in JS. :)
You can either wrap it in another function:
myElem.onclick = function() {
myObjGlobal.func2();
};
Or use .bind, which does effectively the same thing, and which is supported almost universally nowadays:
myElem.onclick = myObjGlobal.func2.bind(myObjGlobal);
Note also that assigning to onclick is a little rude, since you'll clobber any existing click handler. You may want addEventListener instead.
myElem.onclick = myObjGlobal.func2;
This loses myObjGlobal entirely; myObjGlobal.func2 is just a function, with nothing tying its this to anything. In JavaScript, the this of a function is determined when it’s called, not when it’s defined. This is a fantastic and useful feature of JavaScript that’s much more intuitive than, say, Python. When myElem.onclick is called, it’ll be called with this bound to myElem.
Function.prototype.bind is a utility to do what you’re doing with callFunc1, by the way:
myElem.onclick = myObjGlobal.func2.bind(myObjGlobal);

Node.js modules: correct way to refer to sibling functions

This is my current code:
var PermissionsChecker = {};
PermissionsChecker.check = function(id) {
PermissionsChecker.getPermissions(id);
}
PermissionsChecker.getPermissions = function(id) {
// do stuff
}
Two questions:
Is this the right way to construct node.js functions?
Is that line in .check the correct way to refer to a sibling function?
Thanks!
It's perfectly fine. Some notes:
Sibling function isn't really any standard term for methods of the same object. Minor note, but could cause confusion.
When a function is called as a method on some object, then the value of this inside that function refers to the object on which it was called. That is, calling check like this:
PermissionsChecker.check()
...allows you to write the function like this:
PermissionsChecker.check = function(id) {
this.getPermissions(id);
}
...which is more succinct and probably more common.
Nothing about your question is specific to node.js. This applies to JavaScript in the browser (or anywhere else), too.
You could save some typing by rewriting your example like this:
var PermissionsChecker = {
check: function(id) {
this.getPermissions(id);
},
getPermissions: function(id) {
// do stuff
}
};
So long as the function is called with PermissionsChecker.check(), you can refer to the object with this.
CodePad.
What you've done above is called an object literal, but you could choose the prototypal way also (when you need to instantiate objects - OOP stuff).
You can call this inside to refer to another object property:
PermissionsChecker.check = function(id) {
this.getPermissions(id);
}

javascript anonymous function parameter passing

I have some javascript code (within an object) :
toggle: function() {
var me = this;
var handler = function() { me.progress() };
me.intervalId = setInterval(handler, me.intervalTime);
//...More code
}
I'm kind of new to javascript, so doing the above as far as I can tell actually passes the me variable into anonymous the function. I was wanting to see if there is a more declarative way to do so? I wanted something along the line of:
var handler = (function(o) { o.progress();})(this));
but that doesn't seem to be working... Am I missing something? Is this a case where "this is the way the language works so just declare a local variable and deal with it"?
UPDATE:
The source to my problem was/is my unclear understanding of scope and closures in javascript. I found this article to help me understand a little more.
You can use ".bind()":
var handler = function() { this.progress(); }.bind(this);
New browsers have "bind()", and the Mozilla docs have a solid implementation you can use to patch older browsers.
The reason
var handler = (function(o) { o.progress();})(this));
doesn't work because it just immediately calls the anon function, therefore immediately calling o.progress() and assigns the return value of the anon function (undefined) to handler. You need to return an actual function from the outer function:
handler = (function(me){
return function(){
return me.progress();
}
}(this));
On the flip side this is equivalent and just as bad looking as bad looking as the variable assignment (but can still be useful, particularly if this needs to be done in a loop, with the changing i rather than the fixed this).
BTW, if the progress function doesn't have any calls to this inside it , just doing handler = this.progress (without the parens) might suffice.
The anonymous function has access to me because it is declared inside of the outer function (the toggle function); it is closed over by the outer function.
Your handler function will be called by setInterval, which passes exactly zero arguments. This means you can't use parameters in the handler function itself.
I you really want to pass me explicitly, you could write a function accepting an parameter, and have that function return an anonymous function without parameters, but which could access the creator function's parameter:
toggle: function() {
var me = this;
var handler = (function (o) { return function() { o.progress() }; })(me);
me.intervalId = setInterval(handler, me.intervalTime);
//...More code
}
But this basically adds a layer of redirection without really making it more legible. Unless you pull that creating function outside:
function createProgressHandler(o) {
return function() {
o.progress();
};
}
// ...
toggle: function() {
var me = this;
var handler = createProgressHandler(me);
me.intervalId = setInterval(handler, me.intervalTime);
//...More code
}
What you have there is a closure. The function that is created and assigned to handler keeps a reference to the me object. This is normal, everyday JavaScript, and that's the way that closures work generally.
Have you tried to return the function like this?
var handler = function(o){
return function(){
o.progress();
}
}(me);
Now you can call:
handler();

Calling a function in javascript object literal notation declaration

I'm trying to call a function within an object literal that I created, using the this keyword. But an error shows up saying this.doTheMove() is not a function:
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', Animation.init, false);
}
var Animation = {
init: function(){
this.doTheMove(); // I'm calling the function here, but it gives an error.
},
doTheMove: function(){
alert('Animation!');
}
}
Why is there an error?
An explanation of what's happening. Pointy's answer is good but I want to explain it more generically. A very good research on this can be found here
An event handler is just a callback. You pass it a function and an event to listen on. Interally all it will do is call that function.
Animation.init is just a getter for that function. Think of it like this:
var callback = Animation.init
animBtn.addEventListener('click', callback, false);
...
// internal browser event handler
handler() {
// internal handler does stuff
...
// Oh click event happened. Let's call that callback
callback();
}
So all you've done is passed in
var callback = function(){
this.doTheMove(); // I'm calling the function here, but it gives an error.
}
By default in javascript this === window. This will refer to the global object if it isn't set to something. The net effect is that window.doTheMove is called. And that function doesn't exist.
In this case since callback is actaully called by an event handler the this object points at the DOM object that triggered the event so your calling node.doTheMove which still doesn't exist.
What you wanted to do is wrap it with a reference to Animation.
var callback = function() {
Animation.init();
}
This is a function execution and it executes init on Animation. When you execute it on an object like that then internally this === Animation as you would expect.
To sum up. The issue here is that Animation.init is just a reference to a function. It has no information about anything else like Pointy mentioned.
You have to change the way you set that up:
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', function() { Animation.init(); }, false);
}
In JavaScript, the fact that a function happens to be defined as part of an object literal really doesn't mean very much (if anything, in fact). The reference to Animation.init does get you to the proper function, but the problem is that when the function is later invoked (in response to an actual "click"), the browser calls the function but has no idea that the object "Animation" should be the this reference. Again, the fact that the function was declared as part of the object is of no importance at all here. Therefore, if you want this to be something in particular of your own choosing, then you have to make sure it's set explicitly in code you control. The solution above is about the simplest way to do it: it handles the "click" events with an anonymous function that does nothing other than invoke the "init" function via an explicit reference through "Animation". That will ensure that this refers to the "Animation" object when "init" runs.
Another alternative would be to use the ".bind()" facility that some browsers and frameworks support:
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', Animation.init.bind(Animation); }, false);
}
The net effect is almost exactly the same: that call to ".bind()" returns a function that invokes the function on which it was called (that being the "init" function in the "Animation" object), and does so with its first argument as the this reference (the "context" object). That's the same thing that we get from the first example, or effectively the same anyway.
Here's another nice approach, I think.
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', Animation.init, false);
};
var Animation = {
init: function(){
Animation.doTheMove(); // This will work, but your IDE may complain...
},
doTheMove: function(){
alert('Animation!');
}
};
You might want to use the portotype base approach:
// generate a prototype object which can be instantiated
var Animation = function() { this.doTheMove(); }
Animation.prototype.doTheMove = function() {
// if the object has only one method, the whole code could be moved to
// var Animation = function() {...} above
alert('Animation!');
}
Animation.prototype.otherMethod = function(param1, param2) {
// ...
}
// run the code onload
window.onload = function(){
var animBtn = document.getElementById('startAnim');
animBtn.addEventListener('click', new Animation(), false);
}
Six and a half years later, but I'm hoping my answer can also provide some insight for current and future developers.
I tend to code using literal objects inside of self defined functions, and the original question posted works just fine if another self-executing function is added along with a try and catch statement.
It's very important to point out that it's all about scope and context.
Please correct any drawbacks or provide more effective suggestions of using this method.
(function() {
console.log(this); // window object
var animation = {
init: function() {
this.doTheMove();
},
doTheMove: function() {
alert("Animation");
console.log(animation); // animation object
}
};
(function() {
try {
console.log("animation.init"); // animation.init function
animation.init();
} catch(e) {
console.log("Error is: " + e);
}
})();
})();

Javascript: How to make a Control send itself in a method

Where SomeMethod could have:
function SomeMethod(item)
{
item.setAttribute('name', item.id);
}
Instead of:
function SomeMethod(itemId)
{
var someItem;
someItem = document.getElementById(itemId);
someItem .setAttribute('name', someItem .id);
}
Silly example, but the idea is not to send in the id itself, but the actual control calling the method. I swear this can be done but have had no luck searching... partially because I'm not even sure what to search on.
I thought it was self, but self doesn't seem to be what I want when the script I have runs.
Use the this Keyword.
You actually don't need to pass this as an argument to your function, because you've got a click event object that you can access. So:
<script>
function clickEventHandler(event) {
if (!event) {
event = window.event; // Older versions of IE use
// a global reference
// and not an argument.
};
var el = (event.target || event.srcElement); // DOM uses 'target';
// older versions of
// IE use 'srcElement'
el.setAttribute('name', el.id);
}
</script>
I tend to use this approach in all function calls from HTML attributes:-
onclick="SomeMethod.call(this)"
Then in the javascript do:-
function SomeMethod()
{
this.setAttribute('name', this.id);
}
This has a distinct advantage when you may also assign directly to event handler properties in Javascript code:-
document.getElementById("someID").onclick = SomeMethod
If SomeMethod took the context element as a parameter it would very awkward to set up:-
function(id) {
var elem = document.getElementById(id)
elem.onclick = function() { SomeMethod(elem); }
}("someID");
Worse yet this would be memory leaking closure.
At this point: SomeMethod(this) - this returns window object so do not use it. The right way to use this keyword is making it context relevant, so use SomeMethod.call(this).

Categories

Resources