I am a proficient c# programmer, and I have a little bit of experience with HTML and Javascript, but I can't seem to figure out objects.
I am attempting to create a loop, which just keeps updating until the counter reaches 5 then stops the loop. but in the Update method counter = NaN.
This is my exact code.
function LoginScreen() {
this.IntervalID = null;
this.counter = 0;
this.Start = function () {
this.IntervalID = setInterval(this.Update, 60 / 1000);
};
this.Stop = function () {
clearInterval(this.IntervalID);
};
this.Update = function () {
this.counter++;
alert(this.counter);
if (this.counter > 5) this.Stop();
};
}
LS = new LoginScreen();
LS.Start();
The problem is scope of the inner this of your Start(), Stop() and Update() functions. Within those functions, this is referring to the function, and not your LoginScreen object.
To get around these problems, I like to use a little self variable to help with scope issues. That way, whatever is referring to the self variable will use the object. Here's what I mean:
function LoginScreen() {
// add this for scope fun...
var self = this;
this.IntervalID = null;
this.counter = 0;
this.Start = function () {
self.IntervalID = setInterval(self.Update, 60 / 1000); // updated...
};
this.Stop = function () {
clearInterval(self.IntervalID); // updated...
};
this.Update = function () {
self.counter++; // updated...
alert(self.counter); // updated...
if (self.counter > 5) self.Stop(); // updated...
};
}
LS = new LoginScreen();
LS.Start();
Hope this makes sense!
You ran into the dreaded Javascript "this" problem.
Try changing your this.Update code to:
var self = this;
this.Update = function () {
self.counter++;
alert(self.counter);
if (self.counter > 5) self.Stop();
};
But wait there is more
This is a very common mistake in Javascript. I think everyone including me did this at one point. It is fairly easy to understand, though.
What's going on
When you call a function as a method of an object, you set its context to the object. That means that this inside this function will point to your context object.
MyObject = {};
MyObject.someValue = 1;
MyObject.myMethod = function() {
console.log(this.someValue); // This will log "1" to the console, as expected
};
Now comes the tricky part: You can change the context of a function. Let's add some more lines of code here:
MyOtherObject = {};
MyOtherObject.someValue = 2;
MyOtherObject.myMethod = MyObject.myMethod;
When you now call MyOtherObject.myMethod you call the same function as if you would call MyObject.myMethod, but in one case this points to MyObject and in the other case it points to MyOtherObject.
Now, when you call setInterval(MyObject.myMethod, 1000), the functions context will be set to window (technically, setInterval calls window.setInterval) and since window does not have a property called someValue, it will just be undefined.
How to fix it
To fix this, you can create another reference to your LoginScreen object. It will always point to the same object inside the entire scope, unless you change it. You can then use it as an alternative to this without worrying about the context.
function LoginScreen() {
this.IntervalID = null;
this.counter = 0;
// Create another reference to "this"
var self = this;
this.Start = function () {
// The function behind "this.Update" will always be called with "window"
// as its context.
this.IntervalID = window.setInterval(this.Update, 60 / 1000);
};
this.Stop = function () {
// This function is called as "self.Stop", therefore "this" points to
// "self", which points to the right object, so you can use "this".
clearInterval(this.IntervalID);
};
this.Update = function () {
// Inside this function, "this" points to "window".
// You therefore have to use the previously declared "self"
// to point to the right object.
self.counter++;
alert(self.counter);
if (self.counter > 5) self.Stop();
};
}
LS = new LoginScreen();
LS.Start();
Further reference
this - Mozilla Developer Network Another explanation of the beviour of this in JavaScript, along with some functions to control it.
Related
Suppose I have a function a:
function a() {
this.b = 1;
this.set = setInterval(function() {console.log(this.b);}, 200);
}
So when a.set() is called the anonymous function will be called. But this won't work as this at that time when the function is triggered points to the window object. Also it's not a good idea to use a.b as there may be multiple instances of a.
What is a good solution to this problem?
Store a reference to this:
function a() {
var self = this;
self.b = 1;
self.set = setInterval(function() {console.log(self.b);}, 200);
}
The anonymous function that you pass to setInterval has access to any variables in its containing scope, i.e., any local variables of function a(). The magic of JS closures keeps these variables alive even after a() has completed, and each invocation of a() gets its own closure.
Since we have ES6 now, I think we need another answer here:
Use an arrow function:
function a() {
this.b = 1;
this.set = setInterval(() => {console.log(this.b);}, 200);
}
Arrow functions, opposite to normal functions, don't have a this context on their own. This means you have access to the outer this.
This would be the cleanest solution, since most of the time you actually want to switch the this context for your consecutive method calls:
// store scope reference for our delegating method
var that = this;
setInterval(function() {
// this would be changed here because of method scope,
// but we still have a reference to that
OURMETHODNAME.call(that);
}, 200);
Just save your this reference in some other variable, that is not overridden by the window-call later on. Later you can use that variable to reference he object you started with.
function a() {
this.b = 1;
var that = this;
this.set = setInterval(function() {console.log(that.b);}, 200);
}
In your case, you can simply:
function a() {
var _this = this;
this.b = 1;
this.set = setInterval(function () {
console.log(_this.b);
}, 200);
}
Normally, we can also have a helper method Function.prototype.bind to fix the this reference.
This question is waay too old, but I did not like the solutions in here as the idea has mostly been about attaching the instance to something public.
Here is another, working idea:
The problem is that when calling as a callback from an interval, the scope is not inside this. However, you can kinda force it to be by defining a Function variable.
function a() {
var localCallback: () => {
// access `this` as you will
console.log(this);
};
this.timer = setInterval( localCallback, this.saveInterval );
}
Hope this is helpful!
This question already has answers here:
JavaScript setInterval and `this` solution
(9 answers)
Referencing "this" inside setInterval/setTimeout within object prototype methods [duplicate]
(2 answers)
How to access the correct `this` inside a callback
(13 answers)
Closed 5 months ago.
I have a problem in regard to setInterval that I can't figure out.
There is the problem with the scope when calling setInterval or timeout from within an object, but still I can't wrap my head around it.
I tried to put my stuff inside an anonymous function, it won't work.
This is basicly my problem, simplified to the bare bones:
function Scenario(){
var ships = [];
this.ini = function(){
for (var i = 0; i < ships.length; i++){
timeoutID1 = setTimeout(ships[i].ding, 1000);
timeoutID2 = setTimeout(ships[i].bing, 1000);
}
}
this.setShips = function(){
var ship = new Ship("ship");
ships.push(ship);
}
function Ship(name){
this.name = name;
this.ding = function(){
intervalID1 = setInterval(function(){
console.log("ding");
}, 500)
}
this.bing = function(){
var _this = this;
intervalID2 = setInterval(function(){
console.log(_this.name);
}, 500)
}
}
this.setShips();
}
var scenario = new Scenario();
scenario.ini();
http://jsfiddle.net/ancientsion/xkwsn7xd/
Basicly, console.log("ding") works, console.log(_this.name) doesn't.
Why?
This is your problem simplified to bare bones:
var ship = {
name: 'Sheep',
ding: function() {
console.log(this.name);
}
}
setTimeout(ship.ding, 1000); // doesn't work correctly
It may help to see another example to understand why the above doesn't work:
var ding = ship.ding;
ding(); // doesn't work either
In JavaScript this depends on how you call your function. ship.ding() will set this to the sheep object. Bare ding() call will set this to the window object.
You can bind the function to the object you want by using .bind() method. (Function.prototype.bind())
var ding = ship.ding.bind(ship);
ding(); // works
ding is now permanently bound to the sheep object. You can use exactly the same approach with setTimeout:
setTimeout(ship.ding.bind(ship), 1000);
By the time setTimeout() gets around to call your method, it only sees the function and not the invocation context (i.e. the object to bind it to); much like this:
var bing = ships[i].bing;
bing(); // inside bing(), this == window
Basically, you need to provide setTimeout() with a prewired method invocation:
var bound_bing = ships[i].bing.bind(ships[i]);
timeoutID2 = setTimeout(bound_bing, 1000);
The "magic" happens with .bind() as it returns a new function that will have this set up properly.
You should define the value _this in Ship function:
function Ship(name){
this.name = name;
this.ding = function(){
intervalID1 = setInterval(function(){
console.log("ding");
}, 500)
}
var _this = this;
this.bing = function(){
intervalID2 = setInterval(function(){
console.log(_this.name);
}, 500)
}
}
Hoist _this out of the bing function.
you should have
_this = this,
this.bing = function() {
intervalID2 = setInterval(function(){
console.log(_this.name);
}, 500)
}
Javascript is imperative so you need to follow the execution path carefully. In function Ship, this points to a Ship object, in function bing, this points to the global scope (window). You need to save a reference to this so that you can refer back to it in these types of functions.
You put ships[i].bing in setTimeout turns out that the caller of bing is not ships[i] but global, so _this is pointing to global actually.
I am relatively new to JavaScript, and am having trouble grasping why this error occurs:
TypeError: Attempted to assign to readonly property. MyTimer.js: 35
I understand that this error is displayed because I am using strict mode, but I enabled strict mode to help me debug this Object.
The call to create the MyTimer singleton is:
var simTimer = new SimTimer();
Then I add a task to be executed in MyTimer as follows:
var task = function(){
console.log("performing task.");
};
simTimer.addTask(task);
Lastly, this is the MyTimer Object (line 35 is marked):
var MyTimer = (function () {
"use strict";
// var timer;
var tasks;
/**
* If an instance of MyTimer exists, it will be saved to this variable and returned
* by any subsequent calls to this constructor, thus only one instance (stored in this
* variable) will be accessible.
* #private
*/
var instance;
/**
* Called to initialize this MyTimer Object, or return #instance if it already contains
* an instance of this Object.
*/
function Singleton() {
if (instance) {
return instance;
}
instance = this;
tasks = $.Callbacks();
this.timer = setInterval(function()
{
this.tasks.fire();
}, 1000);//<---- THIS IS LINE 35!
this.addTask = function(task)
{
this.tasks.add(task);
};
this.removeTask = function(task)
{
this.tasks.remove(task);
};
}
//instance accessor
Singleton.getInstance = function () {
return instance || new Singleton();
};
return Singleton();
}());
What have I failed to grasp? I have read through a lot of documentation on Module Patterns, and have successfully written Singletons before - so where do I go wrong here?
** EDIT: **
I was able to get the correct behavior by removing var tasks, and creating it within Singleton using this. The working version of the function now looks like this:
function Singleton() {
if (instance) {
return instance;
}
instance = this;
this.tasks = $.Callbacks();
this.timer = setInterval(function(){
instance.tasks.fire();
}, 1000);
this.removeTask = function(task)
{
instance.tasks.remove(task);
};
this.addTask = function(task)
{
instance.tasks.add(task);
};
}
So I still don't fully understand - why did this change fix it? Was it a scope issue after all?
If I am reading your code right, you have a scope issue here
this.timer = setInterval(function()
{
this.tasks.fire(); <-- this will be in window scope
}, 1000);
It should be
this.timer = setInterval(function()
{
instance.tasks.fire();
}, 1000);
I believe the following strict mode restriction is the explanation.
If this is evaluated within strict mode code, then the this value is
not coerced to an object. A this value of null or undefined is not
converted to the global object and primitive values are not converted
to wrapper objects. The this value passed via a function call
(including calls made using Function.prototype.apply and
Function.prototype.call) do not coerce the passed this value to an
object (10.4.3, 11.1.1, 15.3.4.3, 15.3.4.4).
When you call
return Singleton();
The this value will actually be undefined. Try doing this.
return {
getInstance: function () {
return instance || new Singleton();
}
};
Not sure if this is the cause, but looks like the line that reads "tasks = $.Callbacks();" should be "this.tasks = $.Callbacks();". Also, as you are providing an instance as a callback, you will loose the 'this' binding. You function bodys that call anything with "this." should instead use a var that captures this in the outer closure (which looks like 'instance' does.
So for example, the method in question would read:
this.timer = setInterval(function()
{
instance.tasks.fire();
}, 1000);
I think I'm missing some key concept regarding objects and prototype functions in JavaScript.
I have the following:
function Bouncer(ctx, numBalls) {
this.ctx = ctx;
this.numBalls = numBalls;
this.balls = undefined;
}
Bouncer.prototype.init = function() {
var randBalls = [];
for(var i = 0; i < this.numBalls; i++) {
var x = Math.floor(Math.random()*400+1);
var y = Math.floor(Math.random()*400+1);
var r = Math.floor(Math.random()*10+5);
randBalls.push(new Ball(x, y, 15, "#FF0000"));
}
this.balls = randBalls;
this.step();
}
Bouncer.prototype.render = function() {
this.ctx.clearRect(0, 0, 400, 400);
for(var i = 0; i < this.balls.length; i++) {
this.balls[i].render(this.ctx);
}
}
Bouncer.prototype.step = function() {
for(var i = 0; i < this.balls.length; i++) {
this.balls[i].yPos -= 1;
}
this.render();
setTimeout(this.step, 1000);
}
I then create an instance of Bouncer and call its init function like so:
$(function() {
var ctx = $('#canvas')[0].getContext('2d');
var width = $('#canvas').width();
var height = $('#canvas').height();
var bouncer = new Bouncer(ctx, 30);
bouncer.init();
});
The init() function will call step which has a setTimeout to loop the step function.
This works on the first call to step(). However, on the second call (when setTimeout fires step) the instance variable "balls" is undefined. So, in my step function, the second call will blow up saying there is no "length" property for undefined.
Why do I lose my instance information when calling step from setTimeout()?
How could I restructure this so I can loop via a timeout and still have access to those instance variables?
When you call setTimeout(this.step, 1000);, the step method loses its desired context of this, as you're passing a reference to the step method. In the way that you're doing it now, when this.step gets called through setTimeout, this === window rather than your Bouncer instance.
This is easy to fix; just use an anonymous function, and keep a reference to this:
Bouncer.prototype.step = function() {
var that = this; // keep a reference
for(var i = 0; i < this.balls.length; i++) {
this.balls[i].yPos -= 1;
}
this.render();
setTimeout(function () {
that.step()
}, 1000);
}
When you call a Javascript function, the value of this is determined by the call site.
When you pass this.step to setTimeout, the this is not magically preserved; it just passes the step function itself.
setTimeout calls its callback with this as window.
You need to create a closure that calls step on the right object:
var me = this;
setTimeout(function() { me.step(); }, 500);
For more information on the difference between this and closures, see my blog.
This is fairly standard 'this' scope issues. Many, many questions on SO regarding mis-understanding the context of 'this' when executing functions. I recommend you read-up on it.
However, to answer your question, it works because you are calling this.step(), and 'this', in that context, is your desired Bouncer instance.
The second (and subsequent) times it does not work, because when you specify a function to be invoked by setTimeout, it is invoked by the 'window' context. This is because you are passing a reference to the step function, and context is not included in that reference.
Instead, you can maintain context by calling it from the correct scope, from inside an anonymous method:
var self = this;
setTimeout(function(){ self.step(); }, 1000);
Others pointed out the calling context issues, but here's a different solution:
setTimeout( this.step.bind( this ), 1000 );
This uses the ECMAScript 5 bind()[docs] method to send a function with the calling context bound to whatever you pass as the first argument.
If support for JS environments that don't support .bind() is needed, the documentation link I provided gives a solution that will be sufficient for most cases.
From the docs:
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") // closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be fBound is not callable");
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP ? this : oThis || window, aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
This will add the .bind() shim to all functions via Function.prototype if it doesn't already exist.
I'm fairly sure that anything executed by setTimeout happens in global scope, so the reference to this no longer points to your function, it points to window.
To fix it, just cache this as a local variable inside step, then reference that variable in your setTimeout call:
Bouncer.prototype.step = function() {
for(var i = 0; i < this.balls.length; i++) {
this.balls[i].yPos -= 1;
}
this.render();
var stepCache = this;
setTimeout(function () { stepCache.step() }, 1000);
}
This is a closure issue as indicated by #SLaks.
Try this:
Bouncer.prototype.step = function() {
for(var i = 0; i < this.balls.length; i++) {
this.balls[i].yPos -= 1;
}
var self = this;
this.render();
setTimeout(function() {self.step();}, 1000);
}
I have the following code example to use an object that receives the action from the callback. Doesn't seem like this is a good design pattern. Or is it?
When setTimeOut() fires on the function after 1 second, it uses the objInstance global variable (DOM scope) to access the ClassExample object instance. Can someone recommend a better way to utilize callbacks within an object oriented design?
The whole idea is so I can use the callback to update data within my object instance (increment a variable for example).
function ClassExample{
this.initiate = function() {
setTimeOut(objInstance.afterTimeOut,1000); //using the objects global handle
}
this.afterTimeOut = function() {
alert("Received!");
}
}
var objInstance = new ClassExample(); //instance
objInstance.initiate();
No, you're not. You'll want to do this:
this.initiate = function() {
setTimeOut(objInstance.afterTimeOut,1000); //using the objects global handle
}
Now, if "afterTimeout" needs the proper object context, you could do this:
this.initiate = function() {
var instance = this;
setTimeout(function() { instance.afterTimeOut(); }, 1000);
}
OK well you changed the question considerably with that little edit :-) If I were you, I'd just do this (like my original second example):
this.initiate = function() {
var instance = this;
setTimeout(function() { instance.afterTimeOut(); }, 1000);
}
Then you don't need any ugly global variables around at all.
edit — Stackoverflow user #Christoph comments that this isn't particularly pretty. One thing that might help would be to use a "bind" facility, as provided by newer browsers natively (as a method on the Function prototype) or by some libraries (Prototype or Functional for example). What "bind" lets you do is create a little wrapper function like I've got above:
this.initiate = function() {
setTimeout(this.afterTimeOut.bind(this), 1000);
}
That call to "bind" returns a function that is effectively the same sort of thing as the little wrapper I coded explicitly in the example.
function ClassExample{
this.afterTimeOut = function() {
alert("Received!");
}; // Don't forget these
setTimeOut(afterTimeOut, 1000); // Don't use () if you're passing the function as an argument
}
var objInstance = new ClassExample(); //instance
That way you don't need the initiate() method.
If you really want the initiate() method, I'd do it like this:
function ClassExample{
var self = this;
self.afterTimeOut = function() {
alert("Received!");
};
self.initiate = function() {
setTimeOut(self.afterTimeOut, 1000);
};
}
var objInstance = new ClassExample(); //instance
objInstance.initiate();
This is how I'd do it to allow timer reuse and minimize the number of closures:
function Timer(timeout, callback) {
this.timeout = timeout;
this.callback = callback;
}
Timer.prototype.run = function(thisArg /*, args... */) {
var argArray = Array.prototype.slice.call(arguments, 1);
var timer = this;
setTimeout(function() {
timer.callback.apply(thisArg, argArray);
}, timer.timeout);
};
var timer = new Timer(1000, alert);
timer.run(null, 'timer fired!');
And just for fun, a golfed version which is functionally equivalent, but replaces the object with a closure:
function delay(func, timeout) {
return function() {
var self = this, args = arguments;
setTimeout(function() { func.apply(self, args); }, timeout);
};
}
delay(alert, 1000).call(null, 'timer fired!');
You are right it is not the optimal way of doing what you are aiming for. however i have to wonder why you need to break the callstack as part of the initiation, it seems very academic.
apart from that if i had to do that, i'd probably use a closure like so:
function ClassExample{
this.initiate = function() {
setTimeOut((function(self) { return function() { self.afterTimeout();}})(this),1000); //using the objects global handle
}
this.afterTimeOut = function() {
alert("Received!");
}
}
var objInstance = new ClassExample(); //instance
objInstance.initiate()
this.initiate = function() {
var instance = this;
setTimeOut(function() {
instance.afterTimeOut();
}, 1000);
};
By saving this to a local variable, you can avoid using the global handle at all. Also this prevent the afterTimeout() from losing it's this.
Building on Znarkus answer...
I really don't know in which environment his code is running but for me the first approach just do not works. I got: 'ReferenceError: afterTimeOut is not defined'...
The second one, nevertheless, is really cool... I just changed setTimeOut for setTimeout (using lowercase 'o') and included parenthesis after the class name definition turning the first line of code into 'function ClassExample(){'; solved my problem.
My snippet of example code:
Oop with private behaviour, intern callback calling and etc.
function MyTry (name){
// keep this object pointer... that's the trick!
var self = this;
// create private variable
var d = new Date()toJSON().slice(0, 10);
// create a private function
function getName(){return name}
// create public access method
self.hello = function(){alert('Hello '+getName()+'!\nToday is: '+d)}
// note instance method hello passed as a callback function!
self.initiate = function(){setTimeout(self.hello, 3000)}
}