This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 3 years ago.
Why is this.tick not a number in this script?
var universe;
$(function() {
universe = new Universe();
universe.timeStart();
});
function Universe() {
this.timer = 0;
this.tick = 0;
this.timeStart = function(){
this.timer = setInterval(this.timeForward, 500);
};
this.timeStop = function() {
clearInterval(this.timer);
};
this.timeForward = function(){
this.tick++;
console.log(this.tick);
};
}
https://jsfiddle.net/qhL492w1/
Because you didn't use an arrow function to preserve the execution context that the keyword this points to, so once the function that you're making setTimeout/setInterval call gets triggered, this will be the global scope.
(if you're running this in the browser, this will be window by the time the timeout/interval resolves. You can verify that by doing a console.log(this) in your code).
If you need this to stay the same, use an arrow function to make sure that this keeps pointing at what it's pointing to when you declare the code, rather than when the code executes: setInterval(() => this.timeForward(), 500).
Also, modern browsers support modern JS, with consts and class syntax, so what you're doing in a function may make more sense written as:
class Universe() {
constructor() {
this.timer = 0;
this.tick = 0;
}
timeStart(){
this.timer = setInterval(() => this.timeForward(), 500);
}
timeStop() {
if (this.timer) {
clearInterval(this.timer);
}
}
timeForward() {
this.tick++;
console.log(this.tick);
}
}
const universe = new Universe();
universe.timeStart();
And then loading that with a <script src="yourfile.js" defer></script>, where the defer keyword basically does the same thing that jQuery's $(...) does: it runs the code in your file only after the DOM is done (but before DOMContentLoaded gets triggered), no matter where in the HTML you place that script tag.
Related
I'm trying to cancel a requestAnimationFrame loop, but I can't do it because each time requestAnimationFrame is called, a new timer ID is returned, but I only have access to the return value of the first call to requestAnimationFrame.
Specifically, my code is like this, which I don't think is entirely uncommon:
function animate(elem) {
var step = function (timestamp) {
//Do some stuff here.
if (progressedTime < totalTime) {
return requestAnimationFrame(step); //This return value seems useless.
}
};
return requestAnimationFrame(step);
}
//Elsewhere in the code, not in the global namespace.
var timerId = animate(elem);
//A second or two later, before the animation is over.
cancelAnimationFrame(timerId); //Doesn't work!
Because all subsequent calls to requestAnimationFrame are within the step function, I don't have access to the returned timer ID in the event that I want to call cancelAnimationFrame.
Looking at the way Mozilla (and apparently others do it), it looks like they declare a global variable in their code (myReq in the Mozilla code), and then assign the return value of each call to requestAnimationFrame to that variable so that it can be used any time for cancelAnimationFrame.
Is there any way to do this without declaring a global variable?
Thank you.
It doesn't need to be a global variable; it just needs to have scope such that both animate and cancel can access it. I.e. you can encapsulate it. For example, something like this:
var Animation = function(elem) {
var timerID;
var step = function() {
// ...
timerID = requestAnimationFrame(step);
};
return {
start: function() {
timerID = requestAnimationFrame(step);
}
cancel: function() {
cancelAnimationFrame(timerID);
}
};
})();
var animation = new Animation(elem);
animation.start();
animation.cancel();
timerID; // error, not global.
EDIT: You don't need to code it every time - that's why we are doing programming, after all, to abstract stuff that repeats so we don't need to do it ourselves. :)
var Animation = function(step) {
var timerID;
var innerStep = function(timestamp) {
step(timestamp);
timerID = requestAnimationFrame(innerStep);
};
return {
start: function() {
timerID = requestAnimationFrame(innerStep);
}
cancel: function() {
cancelAnimationFrame(timerID);
}
};
})();
var animation1 = new Animation(function(timestamp) {
// do something with elem1
});
var animation2 = new Animation(function(timestamp) {
// do something with elem2
});
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 4 years ago.
I am trying to understand the bind method and I have written the below code :
//
//Window Context
function Hello(d) {
//Always this reffers to calling context
console.log(d);
}
Hello("ABC");
function Student(sname) {
this.name_n = sname;
this.hello = Hello;
this.printAfter2Seconds = printAfter2Seconds.bind(this);
this.print = function() {
console.log(`Student Name: ${this.name_n}`);
}
}
printAfter2Seconds = function() {
console.log(`Before Set TimeOut - ${this.name_n}`);
//let that = this;
setTimeout(function() {
//console.log(this);
console.log(`After Set TimeOut - ${this.name_n}`);
},2000);
}
function Department(dname) {
this.name_n = dname;
this.hello = Hello;
this.printAfter2Seconds = printAfter2Seconds.bind(this);
}
let s = new Student("ABC");
s.hello(s.name_n);
s.printAfter2Seconds();
let d = new Department("IT");
d.hello(d.name);
d.printAfter2Seconds();
//
If I comment the setTimeout line and the line ending setTimeout like below :
//setTimeout(function() {
//console.log(this);
console.log(`After Set TimeOut - ${this.name_n}`);
// },2000);
I am getting the expected output ABC and IT. But If I include setTimeout I am getting undefined both time. So I am guessing some where I need to invoke bind again. This may not be a trivial example that you use everyday just trying to understand bind.
So I need to understand how to bind the this context of the function inside setTimeout or that is even possible.
Thanks in Advance.
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
Below is my javascript code:
function showBranch_init() {
var id_arr = ["jdc_b1","jdc_b2","jdc_b3","jdc_b4"];
for(a=0;a<id_arr.length;a++){
timeoutID = window.setTimeout(function() {
showBranch(id_arr[a]); // <-- Right here
}, 500);
}
}
How can I pass the value of id_arr[a] to showBranch funcion?
Currently the above code returns null for id_arr[a]
by introducing a new scope (by a function call) for each iteration step you can pass the argument like this:
function showBranch_init() {
var id_arr = ["jdc_b1","jdc_b2","jdc_b3","jdc_b4"];
for(a=0;a<id_arr.length;a++){
(function(i) {
timeoutID = window.setTimeout(function() {
showBranch(id_arr[i]); // <-- Right here
}, 500*i);
})(a);
}
}
Updated to fullfill the 2nd req: showBranch() in 500ms steps..
http://jsfiddle.net/HXc4d/
function showBranch_init() {
var id_arr = ["jdc_b1","jdc_b2","jdc_b3","jdc_b4"];
for(a=0;a<id_arr.length;a++){
timeoutID = window.setTimeout(function(idvalue) {
showBranch(idvalue);
}(id_arr[a]), 500);
}
}
EDIT: The problem with your solution is the fact that when the code executes (timeout) id_arr no longer exists in the executing scope, thus leading to undefined result. When sending the variable as an argument it "stays" with the funciton itself, regardless of the executing scope.
function showBranch_init() {
var id_arr = ["jdc_b1","jdc_b2","jdc_b3","jdc_b4"];
timeoutID = window.setTimeout(function() {
for(a=0;a<id_arr.length;a++){
showBranch(id_arr[a]); // <-- Right here
}
}, 500);
}
can you do that?? O_O
Since I need to pass an anonymous function to setInterval if I want parameters, I tried using the below code. Originally I had it calling this.countUp, but as that returned NaN I did some reading and found the .call(this) solution on SO. However, when I combined that with the anonymous function (which I admit I'm a bit foggy on), I'm now getting TypeError: this.countUp is undefined in Firebug.
I suppose I don't need to make count accessible, nor the playBeep method, but let's pretend I wanted to so that I can understand what I'm doing wrong with this code.
function workout() {
var beep = new Audio("beep1.wav");
this.timerWorkout; //three timers in object scope so I can clear later from a different method
this.timerCounter;
this.timerCoolDown;
this.count = 0;
this.startWorkout = function() {
alert(this.count);
this.timerWorkout = setTimeout(this.playBeep, 30 * 1000); //workout beep - 30 seconds
this.timerCounter = setInterval(function() {this.countUp.call(this)}, 1000); //on screen timer - every second
}
this.startCoolDown = function() {
this.timerCoolDown = setTimeout(this.playBeep, 10 * 1000); //cooldown beep - 10 seconds
}
this.playBeep = function() {
beep.play(); //plays beep WAV
}
this.countUp = function() {
this.count++;
document.getElementById("counter").innerHTML = this.count;
}
}
var workout1 = new workout()
Inside startWorkout use bind(this) :
this.timerCounter = setInterval(function() {this.countUp()}.bind(this), 1000);
What happens is setInterval is changing the value of this inside the function you provide for it to call. You need to store this in a separate variable to prevent it from getting overridden.
function workout() {
var self = this;
// ...
this.startWorkout = function() {
alert(this.count);
this.timerWorkout = setTimeout(self.playBeep, 30 * 1000); // this method works
this.timerCounter = setInterval(function() {self.countUp}, 1000); // so does this one
}
}
The reason that the variable scope in js is limited on function. So when you are trying to use this inside a nested function, you get a link to another object. Create a variable var that = this; into a higher-level function, and then use it in any nested function that would refer you to the correct context.
I have a variable created with setInterval called cycle1. It is created with setInterval inside of a prototyped function to an object called eGi, which is the same as $_. After creating it, it still fires, but is inaccessible to the rest of the script and the console. When I try to clear this interval from another prototyped function, nothing happens.
eGi.prototype.startGame = function() {
//other code...
if (somethingOrOther) {
var somethingElse = confirm("start interval?");
if (somethingElse) {
this.cycle1 = setInterval($_.cycle,toMS(INTERVAL_SECONDS));
}
} else {
this.cycle1 = setInterval($_.cycle,toMS(INTERVAL_SECONDS));
}
};
then when i try and stop it in another function
eGi.prototype.gameOver = function() {
clearInterval(this.cycle1);
//other code...
if (restart) {
$_.startGame();
} else {
$_.mainMenu();
}
};
It never gets cleared, and seems to be created again in the calling of $_.startGame. I can't even access it from the Chrome console using $_.cycle1 or the eGi instance variable, egi.cycle1. The strange thing is, this works for accessing any other variable that belongs to my eGi object:
var eGi = function(console,cDom,ctxt,game,devMode) {
$_ = this;
this.game = game;
this.cDom = cDom; //specifically, these objects control the canvas' DOM
this.ctxt = ctxt; //and the CanvasRenderingContext2D
}
eGi.prototype.mainMenu = function() {
this.ctxt.fillText("Hello",250,250); //this works just fine
//etc
};
Why isn't it being cleared?
Full code/game here.
It's a nice game...
The problem is you are referring to "this" inside gameover which doesn't contain reference to cycle1 (undefined).
Instead you have to store "cycle1" as part of an object which can be referenced from your other function (in this case gameover)
Making variables as global is not a good thing. Instead you can store the "cycle1" as part of eGi or any such namespace or object.
Refer here (working code): JSFiddle implementation
Javascript code (start and stop are input buttons)
var eGi = {};
$('#start').click(function start() {
var somethingElse = confirm("start interval?");
if (somethingElse) {
eGi.cycle1 = setInterval(function(){
console.log('Running...');
},1000);
}
});
$('#stop').click(function stop(){
clearInterval(eGi.cycle1);
});