Recursive Function, setTimeout, and 'this' keyword in javascript - javascript

Let's say that I want to invoke a function with the onClick method. Like this:
<li class="inline" onclick="mve(this);" >TEMP</li>
AND I have a JS function that looks like this:
function mve(caller){
caller.style.position = "relative";
caller.style.left = (caller.style.left+20) +'px';
var foo = setTimeout('mve(caller)', 2000);
}
My problem is that the element (which caller refers to) is undefined after the initial onClick call. At least this is what Firebug is telling me.
I'm sure it's an easy solution, so how about just a simple explanation of why, and how?
Also if I run it like so:
function mve(caller){
caller.style.position = "relative";
caller.style.left = (caller.style.left+20) +'px';
}
I would think the element would move 20px right on every click, however that is not the case. Thoughts?

setTimeout() executes a string parameter in the global scope so your value of this is no longer present nor is your argument caller. This is one of many reasons NOT to use string parameters with setTimeout. Using an actual javascript function reference like this and it's quite an easy problem to solve getting the arguments passed accordingly:
function mve(caller){
caller.style.position = "relative";
caller.style.left = (caller.style.left+20) +'px';
setTimeout(function() {
mve(caller)
}, 2000);
}
For the second part of your question, caller.style.left is going to have units on it like 20px so when you add 20 to it, you get 20px20 and that's not a value that the browser will understand so nothing happens. You will need to parse the actual number out of it, add 20 to the number, then add the units back on like this:
function mve(caller){
caller.style.position = "relative";
caller.style.left = (parseInt(caller.style.left), 10) +20) + 'px';
setTimeout(function() {
mve(caller)
}, 2000);
}
Something that is missing from this function is a way for it to stop repeating. As you have it now, it goes on forever. I might suggest passing in either a number of iterations like this:
function mve(caller, iterationsRemaining){
caller.style.position = "relative";
caller.style.left = (parseInt(caller.style.left), 10) +20) + 'px';
if (--iterationsRemaining) > 0) {
setTimeout(function() {
mve(caller, iterationsRemaining)
}, 2000);
}
}
Also, you might be curious to know that this isn't really a recursive function. That's because the mve() function calls setTimeout() and then finishes right away. It is setTimeout() that executes the next iteration of mve() some time later and there is no accumulation on the stack frame of multiple function calls and thus no actual recursion. It does look like recursion from a glance at the code, but isn't technically.

It's likely that caller goes out of scope after the first call. You could preserve it by creating a variable with global scope that preserves the value of caller:
var globalCaller;
function onClickEvent(caller) {
globalCaller = caller;
mve();
}
function mve() {
globalCaller.style.position = "relative";
globalCaller.style.left = (caller.style.left+20) +'px';
var foo = setTimeout('mve()', 2000);
}
This is really ugly, though. You'll create much cleaner code by passing the id of the li element, and then calling getElementById() instead. Or better yet, use jQuery and use $("#id") syntax instead.

Related

setInterval does not seem to be calling the callback function [duplicate]

I have a simple javascript class.
One method of this class sets up a timer using setInterval function. The method that I want to call every time the event fires is defined inside the same class.
The question is, how can I pass this method as a parameter to the setInterval function?
One attempt was setInterval('this.showLoading(), 100). But doesn't work. This method access class properties, so I need the 'this' reference.
This is the sample code:
function LoadingPicture(Id)
{
this.imgArray = null;
this.currentImg = 0;
this.elementId = Id;
this.loadingTimer = null;
}
LoadingPicture.prototype.showLoading = function()
{
if(this.currentImg == imgArray.length)
currentImg = 0;
document.getElementById(this.elementId).src = imgArray[this.currentImg++].src;
}
LoadingPicture.prototype.StartLoading = function()
{
document.getElementById(this.elementId).style.visibility = "visible";
loadingTimer = setInterval("showLoading()", 100);
}
setInterval can take a function directly, not just a string.
https://developer.mozilla.org/en/DOM/window.setInterval
i.e.
loadingTimer = setInterval(showLoading, 100);
But, for optimal browser compatibility, you should use a closure with an explicit reference:
var t = this;
loadingTimer = setInterval(function(){t.showLoading();}, 100);
loadingTimer = setInterval("this.showLoading()", 100);
Firstly, don't use string arguments to setInterval/Timeout. It's dodgy in the same way as using eval, and may similarly fail with CSP security restrictions in the future. So instead:
loadingTimer = setInterval(this.showLoading, 100);
However, as you say, this will lose the owner reference so the called function won't see the right this. In the future (the newly-defined ECMAScript Fifth Edition), you will be able to bind the function to its owner with function.bind:
loadingTimer = setInterval(this.showLoading.bind(this), 100);
and if you implement function.bind yourself for browsers that don't yet have it (see the bottom of this answer), you can use this syntax today.
Otherwise, you will need to use an explicit closure, as in the example Computer Linguist just posted.
All the answers above are acceptable. I just wanted to add that the binding of this can also be solved by using an arrow function. For example, these are all equivalent to each other. However, the lexical scope is maintained when using arrow functions:
// Arrow function - my preferred method
loadingTimer = setInterval(() => this.showLoading, 100);
// .bind method
loadingTimer = setInterval(this.showLoading.bind(this), 100);
// Other method
var t = this;
loadingTimer = setInterval(function(){t.showLoading();}, 100);
Hope this helps :D

assigning a function(with arguments) a variable name? [JavaScript]

I've decided to teach myself functional programming.
I've submitted all of my assignments whilst maintaining the rules of functional programming, and my instructor is totally fine with it. To grab inputs, I've been doing something like this:
var getWidth = function(){
return prompt("What is the width?");
};
This works fine but it could be something simpler, like:
var getWidth = prompt("What is the Width?");
The problem with this, is when I call the function getWidth(), it simply runs the function once and assigns it to the getWidth variable. But that's not what I want, I want it to run prompt("What is the Width?") every time getWidth() is called. I've tried to search for it, but I'm not really entirely sure how to phrase it. Google's useful, if you know how to use it.
Well you could use bind()
var hm = window.prompt.bind(window, "simpler?");
console.log(hm());
it could be something simpler, like:
You cannot "simplify" a function
var a = function onePlusTwo() {
return 1 + 2;
};
by writing
var a = 1 + 2;
The two do entirely different things.
The first defines a function (meaning, does not execute it), and assigns the function to the variable a. The function can then be called with a(), which will evaluate to 3, since that is the return value of the function:
alert(a() * 2) // 6
The second (var a = 1 + 2;) merely executes the code which you had in the body of the function. Executing it assigns the result of 1 + 2 to the variable a. This code is "re-usable" only by copying and pasting it elsewhere in your program.
So if you want to define functionality and re-use it, you have no choice but to define it as a function. That's what functions are for!
If you want to take a function and make another function you can use to call that function later, with or without some arguments, then as mentioned in another answer, you can use bind, as in var a = prompt.bind(window, "What is the width?");, and then call it with a(). But this is essentially the same as writing the function out in the way you did in the first example, and is a little bit less readable.
By the way, none of this is "functional programming". Functional programming does not mean just using functions; all programs do that. Functional programming refers to a style of programming involving manipulating, combining, dynamically creating, and passing around functions.
You cannot simplify your function this way. By the first method:
var getWidth = function(){
return prompt("What is the width?");
};
You are returning an executable function, and assigned the function reference to a variable getWidth. However in the second one:
var getWidth = prompt("What is the Width?");
You are returning the result of the function window.prompt.
What the OP might be looking for, is the lambda functional shorthand. It's a really simple bit of code, that let's you right functions much shorter. It's usually, used for anonymous functions, but it could easily be re-purposed here. For example:
function getWidth(){
return prompt("What is the width?");
}
Would be reduced to:
var getWidth() = () => prompt("What is the width?");
Note that the value of prompt is returned automatically, so you don't need to add return to your code.
Parameters could also be specified if desired, like this:
var getWidth = (x) => prompt(x);
And if you use arguments this way, then the () are optional. This would work too:
var getWidth() = x => prompt(x);

Passing variables VS passing values

I would love to understand the difference between:
var the_timeout = setTimeout("alert(the_string);", 60000);
and:
var the_timeout = setTimeout("alert(" + the_string + ");",60000);
I understand that the first passes the variable, and the second passes the value - but what does that mean exactly and why does it matter? Why is the value passed in the second example?
Also (I hope this is the same subject), why does this work:
var timeout=setTimeout("someFunction();", 3000)
While this doesn't:
var timeout=setTimeout(someFunction(),3000);
When calling a function, someFunction() works, so why do I have to add quotes when using setTimeout()?
I think you're confusing the distinction between pass-by-value and pass-by-reference with this. In the examples you mentioned, there is no difference.
However,
var timeout=setTimeout("someFunction();", 3000)
Works and:
var timeout=setTimeout(someFunction(),3000);
Doesn't because in the second case, someFunction() would run so that it can then pass the result/return value to setTimeout. That's why you pass it as a string so that setTimeout can eval it on its own. Unless, of course, if someFunction() itself returns a function that setTimeout can use as a callback.
However, as zerkms pointed out in a comment, you should pass callbacks instead:
var timeout = setTimeout(function() { someFunction(); }, 3000);
This also has the effect that setTimeout can then call the callback whenever it wants. A major benefit is that you can pass any regular function so that you can benefit from the editor you may be using, instead of packing it all into a string:
var myTrigger = function() {
someFunction();
};
var timeout = setTimeout(myTrigger, 3000);
This will parse execute the code within the string, 60 seconds later.
var the_string = "Hello";
setTimeout("alert(the_string);", 60000);
the_string = "Goodbye";
This means alert(the_string) is executed, just as if it was regular code. So it would alert "Goodbye". This is because when the code is eventually executed, the updated value of the_string is used since you are passing the variable.
But this does something subtly different.
var the_string = "Hello";
setTimeout("alert(" + the_string + ");",60000);
the_string = "Goodbye";
Now we are creating a new snippet of code on the fly. The snippet we are creating is alert(Hello);. But Hello is a variable with no value because you didn't get the quotes right.
But lets say you meant this:
var the_string = "Hello";
setTimeout("alert('" + the_string + "');",60000);
the_string = "Goodbye";
Now this will work, because the code it generates is alert('Hello');. Which at first glance appears to do the same thing. But because the generated code now includes literally a hardcoded string, so when the_string changes, the change doesn't make it into generated code because it was hardcoded into the snippet.
Based on that, this is simple:
setTimeout("someFunction();", 3000)
The code in the string is executed after the delay. In this case, someFunction() is executed.
But this is totally different:
setTimeout(someFunction(),3000);
In this case, someFunction() is executed immediately, and it's return value is passed as the first argument to the setTimeout() function. So it won't do what you expect at all.
Most of this has to do with the quirks of eval and generated code, as setTimeout(string,delay) is a form of eval. And none of this is a problem if you don't use eval, and you don't pass a string to setTimeout().
Passing a string to setTimeout leads to eval, eval leads to wierd crazy bugs, wierd crazy bugs lead to pain, pain leads to sufferrriing.
Instead you pass a function instead. It's better in every single way.
// pass an anonymous function to run some code later
var the_string = "Hello";
setTimeout(function() {
alert(the_string);
}, 60000);
the_string = "Goodbye";
// alerts "Goodbye" 60 seconds later
// pass an anonymous function to run some code
setTimeout(function() {
someFunction();
}, 3000);
// or pass a reference to a function to execute, note lack of ()
setTimeout(someFunction, 3000);

How can I wait a while and then change variable?

I have a little code using jquery and I want to wait like, 300ms and then change a variable. I tried setTimeout but it wouldn't work, it just made the variable change instantly.
setTimeout(animationWait = 0, 300);
(I defined animationWait earlier globally in the document) Basically what I'm trying to do is wait for a click to end before another click can be done. So i thought I'd set a variable and then wait 300ms,
$('#up-arrow').live('click', function(e) {
if(animationWait == 0) {
animationWait = 1;
.... /* Code */
}
}
So I need to change the animationWait back to 0 again after a delay to run the code. I've tried a bunch of stuff but it's still not working, any ideas?
You are not using setTimeout quite right. It must be passed either a function name or an anonymous function.
//anonymous function
setTimeout( function() { animationWait = 0 }, 300);
//or give it a function name
function change() { animationWait = 0; }
setTimeout(change, 300);
The parameter what you pass should be an evaluation statement. For me this works fine:
var animationWait = -1; //Initially set the value to -1
alert(animationWait); //alert to see the value
setTimeout("animationWait = 0", 300); //Setting the value to 0
setTimeout("alert(animationWait)", 400); //Alert after 400 ms
The answer to your question as asked is that setTimeout() expects the first parameter to be a function reference - either the name of a function declared elsewhere or an anonymous function. (Or a string, but for several reasons using a string is almost always the wrong solution.) Your code as is simply executes the expression animationWait = 0 and passes the result of that expression (0) to setTimeout().
So to execute your single line of code via setTimeout() I'd recommend wrapping it in an anonymous function and passing that function to setTimeout():
setTimeout(function(){ animationWait = 0; }, 300);
However, if you are doing animation with jQuery its animation methods allow you to pass a callback function to be executed when the animation ends, so you can do this type of processing there rather than independently setting your own timeout. For example, if your animation was like this:
$("#someelement").animate({someproperty:"somevalue"},
300);
Then you could add a callback function that will be called after this animation completes:
$("#someelement").animate(({someproperty:"somevalue"},
300,
function(){ animationWait = 0; });
To my eye this is neater than a separate setTimeout() call, but also it is easier to maintain because the duration only has to be specified in one place rather than you having to sync your own timeout to the animation.
A similar syntax applies to other jQuery methods like .fadeIn(), .hide(), etc. See the .animate() doco for more information and examples.
Have you tried using jQuery function delay() ?
A standard use case would look something like this:
$('item').delay(300).delayedEffectFunction();
Where delayedEffectFunction() is a made-up function you want to be delayed.

In JavaScript, does it make a difference if I call a function with parentheses?

I noticed a difference when calling a function with empty parentheses, or without any parentheses at all. However, I am not passing any arguments to the function so I wondered, what would be the difference between:
window.onload = initAll();
and
window.onload = initAll;
Please explain the principle behind it.
window.onload = initAll();
This executes initAll() straight away and assigns the function's return value to window.onload. This is usually not what you want. initAll() would have to return a function for this to make sense.
window.onload = initAll;
this assigns the actual function to window.onload - this is possible because in JavaScript, as #Felix says, functions are first class objects - without executing it. initAll will be executed by the load event.
You may also see something like this:
window.onload = () => initAll();
This will create a new function that, when called, will call initAll immediately. Parentheses are necessary here for that "call initAll immediately" part to work. But, because it's wrapped in a function, nothing will execute until that outer function itself is called, and you assign the reference of that outer function to window.onload, so initAll will also be executed on the load event.
What Pekka says is correct, but I want to elaborate a little with an example that will help explain to someone who doesn't fully understand function pointers or delegates.
I won't use window.onload because that's a bit contrived to demonstrate. I'll use a simple multiply function to demo instead:
function Multiply(operator, operand) {
return operator * operand;
}
This could equally be written:
Multiply = function(operator, operand) {
return operator * operand;
}
While in the first example, the implication may not be obvious, the second example shows more clearly that we're assigning a function which has 2 parameters to a variable called Multiply, and this concept of functions as assignments is common throughout JavaScript. This is a small demonstration of the fact that functions are "first class citizens", that is, they can be passed around exactly as if we were passing around values.
So now to the difference of assignment:
var operator = 3;
var operand = 4;
var ret = Multiply(operator, operand);
At the point of defining the ret variable, Multiply is executed and the return value is assigned - ret becomes equal to 12.
Let's try that again a different way:
var operator = 3;
var operand = 4;
var ret = Multiply;
Now, at the point of defining ret, ret becomes your Multiply function as opposed to being the result obtained from your Multiply function. Calls to ret() will cause your Multiply function to be executed, and you can call it exactly as if you'd called Multiply(operator, operand):
var out = ret(3, 4);
is the same as
var out = Multiply(3, 4);
You have effectively said that you are going to use ret as a delegate for Multiply(). When calling ret, we're really referring to the Multiply function.
Back to your window.onload. Think of this as:
window.onload = function() {
//Doing what all good window.onload functions should do...
}
initAll = function() {
return 12;
}
So as you can see, window.onload is a function just like any other function, there's nothing special about it. You can assign it a value, assign it a function, null it out if you wish - the point is that there's nothing any more special about window.onload than there is about your own function. The only slightly different thing is that it gets called by the window when it's loaded. [Disclaimer: I've never actually nulled out window functions, so I'm not sure if this will cause negative repercussions. One would hope they check to see if a function is assigned before calling it i.e. if (window.onload) window.onload();].
Now calling initAll() what we're saying is:
window.onload = initAll();
which might as well say:
window.onload = 12;
But when we say initAll without the parentheses, what we're really saying is: I want to replace whatever my window.onload function is, with a new function - i.e. I want to replace it with my initAll function, so that any calls to window.onload runs my initAll code.
So:
window.onload = function() {
//Doing what all good window.onload functions should do...
}
is replaced with:
window.onload = function() {
return 12;
}
So any call to window.onload will execute your initAll function instead of whatever window.onload was originally. You have replaced the original function with your new function.
In fact, you could equally write:
window.onload = function() {
//Write all your init code right in here instead of having a separate
//initAll function.
}
Another example that may demonstrate better is this:
var d = new Date();
var currentTime = d.getTime();
Whatever the time was at the time d is defined ends up assigned to currentTime. Great, but that's only useful if we want to find out what time the function containing that code was called - i.e. at page load time. What if we want the current time any time that currentTime is called?
var currentTime = function() {
var d = new Date();
return d.getTime();
}
var a = currentTime(); //The current time at the point a is defined...
var b = currentTime; //b is a functional reference to currentTime...
var c = b(); //The current time when variable c is defined
var d = c; //The current time when variable c was defined
Notice how we call b() in our c and d assignments exactly as we could call currentTime()?
Functions in javascript are first-class citizens, and as such, can be assigned to other variables or passed around as arguments.
So, when you do
window.onload = initAll;
You are setting the onload property of the window object to reference the initAll function itself.
When you do
window.onload = initAll();
You are setting the onload property to hold the return value of initAll, since it will execute in place on that line.
I'm 6 years late but I feel this could have been explained a lot simpler than the above answers.
So here is the TLDR; or bird's eye view when calling functions using and not using ()'s
Lets take this function for example:
function foo(){
return 123;
}
if you log "foo" - without ()
console.log(foo);
---outout------
function foo(){
return 123;
}
Using no () means to fetch the function itself. You would do this if you want it to be passed along as a callback.
if you log "foo()" - with ()
console.log(foo());
-----output-----
123
Using () after a function means to execute the function and return it's value.
initAll is a reference to a function value and the brackets operator appended to the function name RUNS this function object.
So if you do something like
a = initAll
then a will become the same as initAll - for example you can do a() - but with
a = initAll()
the variable a will get the return value of the executed initAll function

Categories

Resources