`this.some_property` becomes undefined inside anonymous callback function - javascript

So I can't quite figure out why the variable this.tasks becomes undefined inside of the add event listener I have inside of my goal object. I have a feeling it might have something to do with asynchronous programming(which I still don't fully understand). Sorry I'm a bit of a JS noob, but if you guys could explain to me what I'm doing wrong and what might be a better solution that would be awesome! Thanks.
function Goal(name) {
this.gDiv = document.createElement('div');
this.name = name || "goal";
this.tasks = document.createElement('ul');
//Sets the styling and content and adds it to the parent element
this.initialize = function() {
this.gDiv.className = "default";
this.gDiv.setAttribute("id", this.name);
this.gDiv.innerHTML = this.name;
elem.appendChild(this.gDiv);
this.gDiv.parentNode.insertBefore(this.tasks, this.gDiv.nextSibling);
this.tasks.style.display = "none";
};
//Creates a list underneath the a dive associated with the Goal object
this.addTask = function(task) {
var newLi = document.createElement('li');
newLi.innerHTML = task;
this.tasks.appendChild(newLi);
};
this.gDiv.addEventListener('click', function(){
alert(this.tasks);
});
}
Thank you guys! You all answered my question! I'd been scratching my head at this for a while. Kudos to you all!

The scope changes when you enter that anonymous closure and 'this' changes. You can hack around it by doing
var self = this;
And then using self in place of this (eg):
function Goal(name) {
var self = this;
/* ... */
this.gDiv.addEventListener('click', function(){
alert(self.tasks);
});
If you're using jQuery you could do something nicer:
this.gDiv.addEventListener('click', $.proxy(function() {
alert(this.tasks);
}, this));
Either way works just fine.
EDIT: In ES6, arrow functions can be used instead as they don't bind their own "this", so it becomes even simpler:
this.gDiv.addEventListener('click', () => {
alert(this.tasks);
});

Here is a comparison of some methods (including your problem), to give you a taster, and to try and explain things a little.
// This is the problem that you have,
// where `this` inside the anonymous function
// is a different scope to it's parent
function Test1(something) {
// `this` here refers to Test1's scope
this.something = something;
setTimeout(function() {
// `this` here refers to the anonymous function's scope
// `this.something` is `undefined` here
console.log(this.something);
}, 1000);
};
new Test1('Hello');
// This solution captures the parent `this` as `test2This`,
// which can then be used inside the anonymous function
function Test2(something) {
var test2This = this;
this.something = something;
setTimeout(function() {
console.log(test2This.something);
}, 1000);
}
new Test2('World');
// This solution captures `this` as `test3This` in an `IIFE closure`
// which can then be used in the anonymous function
// but is not available outside of the `IIFE closure` scope
function Test3(something) {
this.something = something;
(function(test3This) {
setTimeout(function() {
console.log(test3This.something);
}, 1000);
}(this));
}
new Test3('Goodbye');
// This method requires that you load an external library: jQuery
// and then use it's `$.proxy` method to achieve the basics of
// Test3 but instead of being referred to as `test3This` the
// outer scope `this` becomes the inner scope `this`
// Ahh, that's much clearer?
function Test4(something) {
this.something = something;
setTimeout($.proxy(function() {
console.log(this.something);
}, this), 1000);
}
new Test4('Mum');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
// This is approximately what jQuery's `$.proxy` does
// but without having to load the whole library
function Test5(something) {
this.something = something;
setTimeout((function(func, context) {
return function() {
func.call(context);
};
}(function() {
console.log(this.something);
}, this)), 1000);
}
new Test5('Dad');
// Lets create the proxy method as a reuseable
function proxy(func, context) {
var args = Array.prototype.slice.call(arguments, 2);
return function() {
return func.apply(
context,
args.concat(Array.prototype.slice.call(arguments))
);
};
}
// and now using it
function Test6(something) {
this.something = something;
setTimeout(proxy(function() {
console.log(this.something);
}, this), 1000);
}
new Test6('Me want cookies');
Then we have Function#bind
function Test7(something) {
this.something = something;
setTimeout(function() {
// `this` was bound to the parent's `this` using bind
console.log(this.something);
}.bind(this), 1000);
};
new Test7('Num num');
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.min.js"></script>
And most recently ES2015 Arrow functions
function Test8(something) {
this.something = something;
setTimeout(() => console.log(this.something), 1000);
};
new Test8('Whoop');

In ES6, arrow functions were introduced, which do not bind their own this.
MDN for reference.
So creating an anonymous function using the arrow syntax is probably the easiest way to overcome this issue nowadays. It is supported by all major browsers currently, except IE.

the keyword 'this' changes in it's meaning for an event handler against a constructor
please refer to the MDN
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#As_a_DOM_event_handler

Related

How Object.prototype and bind() method work together

I'm reading about ngInfiniteScroll, and i'm a newbie about JS.
As I've read the demo of nfInfiniteScroll, it's hard for me to understand why Reddit.nextPage have been transformed into Reddit.prototype.nextPage and it has been used the bind() method to wrap a part of Reddit.prototype.nextPage body.
Here is the code.
myApp.controller('DemoController', function($scope, Reddit) {
$scope.reddit = new Reddit();
});
// Reddit constructor function to encapsulate HTTP and pagination logic
myApp.factory('Reddit', function($http) {
var Reddit = function() {
this.items = [];
this.busy = false;
this.after = '';
};
Reddit.prototype.nextPage = function() {
if (this.busy) return;
this.busy = true;
var url = "https://api.reddit.com/hot?after=" + this.after + "&jsonp=JSON_CALLBACK";
$http.jsonp(url).success(function(data) {
var items = data.data.children;
for (var i = 0; i < items.length; i++) {
this.items.push(items[i].data);
}
this.after = "t3_" + this.items[this.items.length - 1].id;
this.busy = false;
}.bind(this));
};
return Reddit;
});
I've just understood: by using this I can have access to properties in Reddit object.
Is it only because var Reddit is assigned an anonymous function and I need to bind this of the anonymous function to this of Reddit.nextPage, so they refer to the same properties?
But I can crearly see it is possible to have access to those properties even without the bind() method. See:
if (this.busy) return;
this.busy = true;
I've read some articles about the topic, but none exaplains it in depth: I'm really confused about.
Lets look at these functions:
Reddit.prototype.nextPage = function() {
// outer function
...
$http.jsonp(url).success(function(data) {
// inner function
}.bind(this));
};
Without binding, this in inner function would have different properties, since it's in another context. But if we call bind(this) we tell inner function to use this from outer function's context.
For more information I recommend this article.
I haven't visited the blog post, but I'm guessing that the reason it has been moved to be declared on the prototype is to have it automatically included in each instance of your "Reddit" service. Every time your service is created it will include this method, as all prototype methods are automatically inherited.
Regarding the bind, whenever you are passing a function as an argument, when the function is to get executed it will lose the main context, meaning it will not be bound to your Reddit service any more, because it will have a new scope of execution. Therefore calls to this.items, this.busy and this.after would all be undefined and would cause errors.
Here's some more info on the bind(), call() and apply().
this is context dependent. An example:
var foo = {
bar: function() {
console.log(this.baz);
},
baz: 3
};
foo.bar(); // logs 3
But in an asynchronous callback the context is gone, an example using setTimeout:
var foo = {
bar: function() {
setTimeout(function() { console.log(this.baz); }, 0);
},
baz: 3
};
foo.bar(); // logs undefined or throws an error in strict mode
'this' is no longer in the context of foo. We can get around that limitation using bind:
var foo = {
bar: function() {
setTimeout((function() { console.log(this.baz); }).bind(this), 0);
},
baz: 3
};
foo.bar(); // logs 3
We've now bound the context to foo (the value of this at the call site) which is what's going on in your example, the binding of this in the callback passed to the success handler of the promise returned by $http.jsonp.

setTimeout() inside JavaScript Class using "this"

I am trying to use setTimeout() inside a class function in JavaScript. The setTimeout() is supposed to trigger another method in the same Class, so the function I am passing it is written as window.setTimeout("this.anotherMethod", 4000). That bring the problem: this references the calling Object, in the case of setTimeout() it is window. How can I use enclosures to return a reference to the Class Object itself?
myObject = function(){
this.move = function(){
alert(this + " is running");
}
this.turn = function(){
alert(this + " is turning");
}
this.wait = function(){
window.setTimeout("this.run" ,(1000 * randomNumber(1,5)));
}
this.run = function(){
switch(randomNumber(0,2)){
case 0:
this.move();
break;
case 1:
this.turn();
break;
case 2:
this.wait();
}
}
}
You can do this:
var that = this;
setTimeout(function () {
that.doStuff();
}, 4000);
You can also bind for more succinct code (as originally pointed out by #Raynos):
setTimeout(this.doStuff.bind(this), 4000);
bind is a standard library function for exactly this coding pattern (ie capturing this lexically).
You can also bind a function to scope.
setTimeout(this.run.bind(this) ,(1000 * randomNumber(1,5)));
Be warned Function.prototype.bind is ES5
this can be problematic in javascript, as you've discovered.
I usually work around this by aliasing this inside the object so that I can use the alias whenever I need a reference back to the containing object.
MyObject = function ()
{
var self = this;
// The rest of the code goes here
self.wait = function(){
window.setTimeout(self.run ,(1000 * randomNumber(1,5)));
}
}
this.wait = function(){
var self = this;
window.setTimeout(function() { self.run() } ,(1000 * randomNumber(1,5)));
}
So you store the reference to the object you're calling .run on in a local variable ('self').
class A{
setTimeout(()=>{
// here this != undefined because of arrow function
},500);
}
this is sensitive to the context in which it is called. When you pass a string to setTimeout then that is evaled in a completely different context.
You need to preserve the current value of this (by copying it to a different variable) and maintain the scope (by not using (implied) eval).
this.wait = function(){
var self = this;
setTimeout(function () { self.run() },
(1000 * randomNumber(1,5))
);
}
At the top of your main myObject make a new reference to the current value of this:
var self = this;
and then create a closure for your timer callback that uses that new reference instead of the global object that setTimeout will use as the default context in callbacks:
setTimeout(function() {
self.run();
}, 4000);
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
inside func, this always refer to the global object. you can pass in the current object into func,
var timeoutID = window.setTimeout(func, delay, this);
function func(that) {...}
unfortunately it does NOT work in IE
Note that passing additional parameters to the function in the first syntax does not work in Internet Explorer.
you can just use the arrow function syntax:
setTimeout(() => {
this.doStuff();
}, 4000);
Have you tried;
window.setTimeout("myObject.run" ,(1000 * randomNumber(1,5)));
You can use this code instead, which works in all modern browsers -
setTimeout(function(thisObj) {thisObj.run();},1000,this);
Ref: http://klevo.sk/javascript/javascripts-settimeout-and-how-to-use-it-with-your-methods/
Shorter way. Without anonymous func.
var self = this;
setTimeout(self.method, 1000);
It is not recommended to use setTimeout or setInterval using strings
setTimeout("myFunction()", 5000);
//this is the same as
setTimeout(function(){ eval("myFunction()"); }, 5000)); //<-- eval == BAD
Ran into a more complex situation...class A has a member of type B and a method that calls setTimeout which calls a method on class B. Solved as follows:
class A {
constructor(b) {
this.b = b;
}
setTimer(interval) {
setTimeout(this.b.tick.bind(this.b), interval);
}
}
class B {
constructor(name){
this.name = name;
this.ele = window.document.getElementById('B');
}
tick() {
console.log(this);
this.ele.innerText += ' ' + this.name;
}
}
Which bound A.b to this within B.tick and worked.
Here's a fiddle with bind: https://jsfiddle.net/jrme9hyh/
And one without bind which fails: https://jsfiddle.net/2jde8tq3/

Pattern to deal with 'this' in JavaScript event handlers

I'm trying to encapsulate some code to grab and release the onLoad event for a tab in a Firefox Extension such that as necessary, I call:
var onLoad = new MyHandler_onLoad();
And then when I'm done, I call:
onLoad.unregister();
In principle, this code implements the above fine until you delve in to the grittier details.
function bind(scope, fn) {
return function(){
fn.apply(scope, arguments);
};
function MyHandler_onLoad()
{
this.scan = function() {
do_scan(this.browser); // this.browser == undefined
};
this.register = function() {
this.tab.addEventListener("load", this.scan, false);
};
this.unregister = function() {
this.tab.removeEventListener("load", this.scan, false);
};
this.tab = gBrowser.selectedTab;
this.browser = gBrowser.selectedBrowser;
this.register();
window.addEventListener("unload", bind(this, this.unregister), false);
};
Due to the behaviour of JavaScript's this, I'm struggling. I want to be able to access this.browser from my scan function, but can't.
I've used bind to ensure that unregister gains the appropriate context on unload. But, I can't do this with the call to scan as I'll not be able to remove it later if I don't have a name.
Is there a good pattern for doing this sort of thing in JavaScript?
I've tried storing the result of bind(this, this.scan) as a variable in the constructor, but it doesn't help and am now struggling for options.
this, in JavaScript, always points to the current object. If there is no current object, this points to window, which is always the top-level scope (in a browser anyway)
By example:
function(){
...
this.foo = function(){
this;
// There is no object here, so `this` points to `window`
}
}
function foo(){
this;
// again, no object so `this` points to window`
}
foo();
function foo(){
this;
// because initialized with `new`, `this` points to an instance of `foo`
this.bar = function(){
this;
// no object, `this` points to `window`
}
}
var foobar = new foo();
// above is roughly equivalent to: foo.apply({}, arguments); Note the new object
foobar.bar();
var foo = {
bar : function(){
this;
// `this` points at `foo` -- note the object literal
}
};
function foo(){
}
foo.prototype.bar = function(){
this;
// `this` points to the instance of `foo`
}
var foobar = new foo();
foobar.bar();
The concept of binding allows you to lock the this variable to whatever you want since the final call to the function is via .apply(scope, params), so going back to your original question, my last example above will work, so will this:
function foo(){
this.scan = bind(this, function(){
this;
// `this` points to instance of `foo` IF `foo` is instantiated
// with `new` keyword.
});
}
new foo()
If you want to understand all of this more, I have two articles I wrote ages back that should help:
http://www.htmlgoodies.com/primers/jsp/article.php/3600451/Javascript-Basics-Part-8.htm
http://www.htmlgoodies.com/primers/jsp/article.php/3606701/Javascript-Basics-Part-9.htm
function MyHandler_onLoad()
{
var self = this;
Having done this, self will always point to the correct object in your handlers.
Solution: Don't use this.
Here is an alternative way to define MyHandler_onLoad
function MyHandler_onLoad() {
var onload_handler = {
scan: function() {
do_scan(onload_handler.browser); // onload_handler.browser == undefined
},
register = function() {
onload_handler.tab.addEventListener("load", onload_handler.scan, false);
},
unregister = function() {
onload_handler.tab.removeEventListener("load", onload_handler.scan, false);
}
};
onload_handler.tab = gBrowser.selectedTab;
onload_handler.browser = gBrowser.selectedBrowser;
onload_handler.register();
window.addEventListener("unload", bind(onload_handler, onload_handler.unregister), false);
return onload_handler;
}
Even better? Move global dependencies up and no access to tab and browser properties (ie making them 'private')
You could even choose to hide register and unregister functions as I'm not sure you even need them, since it seems to attach itself already.
var handler = MyHandler_onLoad(gBrowser.selectedTab, gBrowser.selectedBrowser);
function MyHandler_onLoad(tab, browser) {
var onload_handler = {
scan: function() {
do_scan(browser); // browser == undefined
},
register = function() {
tab.addEventListener("load", onload_handler.scan, false);
},
unregister = function() {
tab.removeEventListener("load", onload_handler.scan, false);
}
};
onload_handler.register();
window.addEventListener("unload", bind(onload_handler, onload_handler.unregister), false);
return onload_handler;
}
Specifically your problem with this is that it points to the scan function, not your handler object. If you don't use this at all then you will never run into these kinds of bugs.
Oh, and you don't need to use new either.

Javascript: How to get a reference to the parent object when "this" has been overwritten with "call"?

Okay, I'm hating Javascript right now, and I hope someone can help me.
I have code which is set up like the following:
function Obj1() {
var me = this;
this.something = "yay";
this.getThis = function(){
return me;
}
}
Obj1.prototype.method = function() {
return this.something;
};
function Obj2() {
this.something = "nay";
}
Obj2.prototype.method = function() {
return this.something;
};
var o1 = new Obj1();
var o2 = new Obj2();
document.write(o1.method()); // Returns yay
document.write(o1.method.call(o2)); // Returns nay, but I need "yay" here
(JSFiddle # http://jsfiddle.net/A9u9K/)
My Problem is, that I need to call Obj1.method in the second case, but I am absolutely unable to get a reference to the object :(
How can I work around this?
Edit: Sorry, I got my example code pretty wrong :( Updated it. I took most of the code from a previous answer, because it is much nicer and still illustrates my problem.
Updated Answer:
document.write(o1.method.call(o2)); // Returns nay, but I need "yay" here
You've said you've got it sorted now, but as the answer to that isn't actually shown here on SO, I figured I may as well update to show it.
If it's method you want to have access me, even if it's been called with a different this value, you have to define it like getThis, as a closure over me:
function Obj1() {
var me = this;
this.something = "yay";
this.method = function() {
return me.something;
};
this.getThis = function(){
return me;
};
}
function Obj2() {
this.something = "nay";
}
Obj2.prototype.method = function() {
return this.something;
};
...or of course, if you don't need the "something" to be a property on the object, just make it a var within the constructor (a private variable, like me):
function Obj1() {
var me = this;
var something = "yay";
this.method = function() {
return something;
};
this.getThis = function(){
return me;
};
}
function Obj2() {
this.something = "nay";
}
Obj2.prototype.method = function() {
return this.something;
};
Original Answer: (To Revision 1 of the question, which didn't have me.)
but I thought that, when creating a closure (as I do in 4) Javascript should preserve "this".
this is set entirely by how a function is called, not where it's defined; more about that here and here. But the way you've defined your getThis function, you can use the fact it closes over the constructor call to solve this (no pun) without using this:
function Obj1() {
var me = this; // <== Use a variable to remember `this`
this.something = "yay";
this.method = function() {
return this.something;
};
this.getThis = function(){
return me; // <== Return it
};
}
Live example
More about closures and the plumbing that makes the me thing work here.
There is a cost involved in this, and just generally in your pattern of defining functions within the constructor function: Each individual object created by Obj1 and Obj2 gets its own copy of each function. This can have memory implications if there are lots of these objects running around (but unless you have lots, you needn't worry and you get benefits like the me thing and other private variables). In constrast, if you use a function assigned to the prototype, all instances will share a single, common copy of the function.
In your sample code, only the getThis function really needs to be duplicated for every instance (because you're relying on the closure), so you can do this to avoid unnecessary function proliferation:
function Obj1() {
var me = this;
this.something = "yay";
this.getThis = function(){
return me;
};
}
Obj1.prototype.method = function() {
return this.something;
};
function Obj2() {
this.something = "nay";
}
Obj2.prototype.method = function() {
return this.something;
};
see it here http://jsfiddle.net/2Jhwv/5/
The issue is with the reference changing for the this object with scope.
Instead if using a this directly in closure use a local variable equated to this, i.e, change your Obj1 toL
function Obj1() {
this.something = "yay";
var that = this;
this.method = function() {
return that.something;
}
this.getThis = function(){
return that;
}
}
The only way to solve this is to another place holder to hold the value of this in Obj1 and use it in the function method() and getThis().
function Obj1() {
var instance = this;
this.something = "yay";
this.method = function() {
return instance.something;
}
this.getThis = function(){
return instance;
}
}
But what I cannot under stand is why you are doing it(obj1.getThis.call(obj2).method())?
This explicitly says that you want to change the scope of the method getThis() to something else, then you are trying to solve the problem which was created by this usage.
Can you tell why you want something like this?

Javascript: How to access a class attribute from a function within one of the class's functions

Within my a certain function of a class, I need to use setInterval to break up the execution of the code. However, within the setInterval function, "this" no longer refers to the class "myObject." How can I access the variable "name" from within the setInterval function?
function myObject() {
this.name = "the name";
}
myObject.prototype.getName = function() {
return this.name;
}
myObject.prototype.test = function() {
// this works
alert(this.name);
var intervalId = setInterval(function() {
// this does not work
alert(this.name);
clearInterval(intervalId);
},0);
}
myObject.prototype.test = function() {
// this works
alert(this.name);
var oThis = this;
var intervalId = setInterval(function() {
// this does not work
alert(oThis.name);
clearInterval(intervalId);
},0);
}
This should work. The anonymous function's "this" is not the same "this" as your myObject's "this."
Here's the prototype bind function
Function.prototype.bind = function( obj ) {
var _this = this;
return function() {
return _this.apply( obj, arguments );
}
}
This is what binding is for in Prototype:
function myObject() {
this.name = "the name";
}
myObject.prototype.getName = function() {
return this.name;
}
myObject.prototype.test = function() {
// this works
alert(this.name);
var intervalId = setInterval(function() {
// this does not work
alert(this.name);
clearInterval(intervalId);
}.bind(this),0);
}
Please note that s13james's answer is incomplete in that bind() is not a standard feature and must be provided elsewhere - one way is using the prototype.js Javascript framework, while another will be to do it yourself using meouw's code example.
If you don't use bind() (which is nifty, I must say) then djangel's response is what you could have done, and is what I most often do.

Categories

Resources