I am trying to undrstand the code
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
from here http://bonsaiden.github.com/JavaScript-Garden/#function.closures
I understood this method :
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
Can anyone please help me by explaining the first one?
I will try to explain how I understands the first one,
first i is 0,
setTimeout is called,
self calling function "function(e)" is called with i=0,
Im stuck!! what happens when this function returns a function?
All the first one does is return a function that will be called after the timeout happens.
The purpose of it is to create a sub-scope for each iteration of the for loop so that the incrementing i isn't overridden with each iteration.
More explanation:
Lets take this apart into two different pieces:
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
This is the first piece:
for(var i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i); //9-9
},1000);
}
Now, when you run this loop, you will always get console.log()'s that contain 9 instead of 0 to 9. This is because each setTimeout is using the same reference to i.
If you wrap the setTimeout part of that in an anonymous function, it creates a scope for each iteration allowing each setTimeout to have it's own i value.
for(var i = 0; i < 10; i++) {
setTimeout((function(i) {
return function() {
console.log(i); // 0-9
}
})(i), 1000)
}
The outer function inside the setTimeout gets executed immediately with an i of 0 for first iteration, 1 for second, etc. That function then in turn returns a function which is the function that setTimeout uses. A function is being generated and returned for each iteration of the loop using a different value for i.
Both end up with the same result: a setTimeout is called with a function to invoke, which writes a number from 0 to 9 on the console. Both use nested functions to get the current value of i into a closure so you don't end up logging 10 9's.
The first code chooses to have a function returning the function that setTimeout will call. The second changes the nesting order so that the closed-over function invokes setTimeout itself. The net effect is the same.
Other than stylistic reasons and personal choice, I don't see a reason to choose one over the other.
"Can you please check the updated question specifying where im getting confused"
OK, here's the long explanation. Remember that the first parameter to setTimeout() needs to be a reference to the function that you want executed after the specified delay. The simplest case is to just name a function defined elsewhere:
function someFunc() {
console.log("In someFunc");
}
setTimeout(someFunc, 100);
Note there are no parentheses on someFunc when passing it as a parameter to setTimeout because a reference to the function itself is required. Contrast with:
setTimeout(someFunc(), 100); // won't work for someFunc() as defined above
With parenthese it calls someFunc() and passes its return value to setTimeout. But my definition of someFunc() above doesn't explictly return a value, so it implicitly returns undefined - which is like saying setTimeout(undefined, 100).
But it would work if changed someFunc() to return a function instead of returning undefined:
function someFunc() {
return function() {
console.log("In the function returned from someFunc");
};
}
So now (at last) we come to the code from your question:
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
Instead of referencing a function by name and calling it as someFunc(i) it defines an anonymous function and calls it immediately as (function(e) {})(i). That anonymous function returns another function and it is that returned function that becomes the actual parameter to setTimeout(). When the time is up it is that returned function that will be executed. Because the (inner) function being returned is defined in the scope of the (outer) anonymous function it has access to the e parameter.
Related
I heard that every function remembers(?) the lexical environment where the function had created.
In this code, The function function () { console.log(i);},
I want to know where this function had been created. If some function is a parameter of other function, where is the created(?)/generated point?
function countSeconds(howMany) {
for (var i =1; i <= howMany; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000 );
}
};
In this code, The function function () { console.log(i);}, I want to know where this function had been created.
This function is defined as a function expression. Such an expression is evaluated at runtime, much like an expression { y: x*x } would be evaluated at runtime. But in this case the evaluation result is a function object. And that function is then passed as argument to setTimeout. This means there are just as many functions created as there are iterations of your for loop.
Now, even though the function expression is evaluated at the moment setTimeout is executed, this does not mean that the body of the function is executed at that same time. It is not. It will only be executed when the timeout expires. At that time the function body executes, and at that time only it will evaluate expressions used in that code, such as variable i. In your example, the variable i will already have reached the value howMany+1, because that for loop already ran to completion before the timer expired, and the callback got called.
If that is something you want to avoid, then use a separate variable i for each iteration of the for loop. With let instead of var you create such distinct variables, which only live inside the for loop's block. And so then each of the function expressions will reference its "own" i:
function countSeconds(howMany) {
for (let i =1; i <= howMany; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000 );
}
};
countSeconds(10);
If some function is a parameter of other function, where is the created(?)/generated point?
Like all arguments to a function call, they're evaluated right before the function is called. You can rewrite this with two temporary variables into the equivalent
function countSeconds(howMany) {
for (var i=1; i<=howMany; i++) {
const __arg1 = function() {
console.log(i);
};
const __arg2 = i * 1000;
setTimeout(__arg1, __arg2);
}
}
The function is created when the function expression is evaluated.
(Also notice that this code has the famous closure in a loop problem.)
I dont understand what you mean?
You mean where the function console.log() ist defined?
class console {
log(text) {
[I dont know]
}
}
Then you can make "console.log()" to call the function "Log" from Claas console? Did you mean that?
for(var i = 2;i >= 1;i--) {
setTimeout(function (i) {
console.log(i);
},1000);
console.log("hii");
}
This code gives me output as
hii
hii
undefined
undefined
Where as after using setTimeout in below format gives different output
for(var i = 2;i >= 1;i--) {
setTimeout(print(i),5000);
console.log("hii");
}
function print(i) {
console.log(i);
}
This code gives me output as
2
hii
1
hii
I don't understand why in second case code is executing synchronously.
Because there you have called the print function, it's not passed as a callback. In this situation, it will complete the execution of print function and then pass the return value as first argument to setTimeout.
This is what you are trying:-
for(var i = 2;i >= 1;i--) {
setTimeout(print(i),5000);
console.log("hii");
}
function print(i) {
return function(){console.log(i)}
}
In the first example, you are passing a function that takes a single parameter, i to setTimeout , but setTimeout does not pass any arguments to that function (or any function for that matter) when it calls it. Hence i is undefined in that function and so undefined is outputted.
In the second example, you are calling print, and passing the result to setTimeout. But print is executing immediately, so that's why a number is outputted before "hii" each iteration.
for(var i = 2;i >= 1;i--) {
setTimeout(function (i) {
console.log(i);
},1000);
console.log("hii");
}
Javascript has a function-level scope and not a block-level scope. so when you call the setTimeout inside the for loop like below:
setTimeout(function (i) {
console.log(i);
},1000);
what happens is a local variable i is introduced which has not been set to any value and hence it returns undefined in JS.
in your second snippet the callback gets executed and returns value which is printed on screen
I'll get to the point: I have this loop:
for (var i = 1; i <= toSchedule; i++) {
when = trackWrapper.lastPlay +
(trackDuration +
(looper.timeInterval - trackDuration));
track.play(true, when);
trackWrapper.lastPlay = when;
}
The play method has this inside the body:
[...]
// Here when is a different value for each call (verified)
// Many calls of the play method are performed before the returned function below is run as a callback
function playingCallback(myWhen){
return function(buffers){
// Here myWhen will always be equal to the value of the last call occurred BEFORE the first callback execution
console.log("myWhen = "+myWhen);
[...]
};
};
var realCallback = playingCallback(when);
track.scheduled.push(when);
track.recorder.getBuffer(realCallback);
So, for example:
play(true, 1);
play(true, 2);
play(true, 3);
// Wait for it...
myWhen = 3;
myWhen = 3;
myWhen = 3;
Now: I've read about closures, I've read about the "infamous loop problem", I've read tens of answers here on StackOverflow but I couldn't figure this out. It's the second time I have this kind of problem with callbacks so, at this point, I guess I haven't completely understood what is going on.
Could you please explain to me what is supposed to be wrong with the code above? Thank you in advance.
Generally you should understand the following rule: A clousure will have access to its "surrounding scope", even after the scope has been exited. But it will be the state of the scope at execution time and not(!) at creation time of the closure
If you create a closure inside a loop, it will have access to the loop variable. But the loop will most likely already have ended. So the loop variable will hold the value of its last loop.
So if your closure is a callback you should create a copy of your relevant scope variable(s) at creation time and use this copy at execution time. You can do this (for example) by creating an inner closure from an immediately executing anonymous function
function myOuterScope(count) {
for(i=0; i<count; i++) {
setTimeout((function(local_i) {
// this function will be immediately executed. local_i is a copy of i at creation time
return function() {
// this is the function that will be called as a callback to setTimeout
// use local_i here, and it will be 0, 1, 2 instead of 3, 3, 3
}
})(i)
, 1000);
}
}
myOuterScope(3);
I am looking at a javascript code that manipulates an HTML A tag , and I'm having trouble understanding how it sets up the "onclick" property. It seems to be telling it to update ytplayer_playitem with the index variable j and then call ytplayer_playlazy(1000)
But what's up with all the parentheses? What details in the javascript syntax allows it to be setup like this?
var a = document.createElement("a");
a.href = "#ytplayer";
a.onclick = (function (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
Well, basically, the value of onclick is a function that will get called when the element is clicked. Whatever you want to happen when the user clicks the element goes in the body of the function.
You could create a named function and then assign it to the element's onclick attribute:
function youClickedMe() {
...
}
a.onclick = youClickedMe
but that clutters up the namespace with a function name that is never referenced anywhere else. It's cleaner to create an anonymous function right where you need it. Normally, that would look like this:
a.onclick = function() { ... }
But if we try that with your specific example:
a.onclick = function() {
ytplayer_playitem = something; // ??
ytplayer_playlazy(1000);
}
We see that it hard-codes the something that gets played. I'm assuming the original code was taken from a loop which generates several clickable links to play; with the code just above, all of those links would play the same thing, which is probably not what you want.
So far, so straightforward, but this next leap is where it gets tricky. The solution seems obvious: if you're in a loop, why not just use the loop variable inside the function body?
// THIS DOESN'T WORK
a.onclick = function() {
ytplayer_playitem = i;
ytplayer_playlazy(1000);
}
That looks like it should work, but unfortunately the i inside the function refers to the value of the variable i when the function is called, not when it's created. By the time the user clicks on the link, the loop that created all the links will be done and i will have its final value - probably either the last item in the list or one greater than that item's index, depending on how the loop is written. Whatever its value is, you once again have the situation where all links play the same item.
The solution in your code gets a little meta, by using a function whose return value is another function. If you pass the loop control variable to the generating function as an argument, the new function it creates can reference that parameter and always get the value that was originally passed in, no matter what has happened to the value of the outer argument variable since:
function generate_onclick(j) {
// no matter when the returned function is called, its "j" will be
// the value passed into this call of "generate_onclick"
return function() { ytplayer_playitem = j; ytplayer_playlazy(1000); }
}
To use that, call it inside the loop like this:
a.onclick = generate_onclick(i);
Each generated function gets its very own j variable, which keeps its value forever instead of changing when i does. So each link plays the right thing; mission accomplished!
That's exactly what your posted original code is doing, with one small difference: just like the first step in my explanation, the author chose to use an anonymous function instead of defining a named one. The other difference here is that they are also calling that anonymous function immediately after defining it. This code:
a.onclick = (function (j) { ... })(i)
is the anonymous version of this code:
function gen(j) { ... }
a.onclick = gen(i)
The extra parens around the anonymous version are needed because of JavaScript's semicolon-insertion rules; function (y) {...}(blah) compiles as a standalone function definition followed by a standalone expression in parentheses, rather than a function call.
"But what's up with all the parentheses? "
Most of the parentheses are just doing what you'd expect.
There's an extra set that isn't technically needed, but is often used as a hint that the function is being invoked.
// v-v---these are part of the function definition like normal
a.onclick = (function (j) {
// ^-----------this and...v
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
// v---...this are technically not needed here, but are used as a hint
})(i);
// ^-^---these invoked the function like normal
"What details in the javascript syntax allows it to be setup like this?"
The upshot is that the function is invoked immediately, and passed i so that its value is referenced by the j parameter in the immediately invoked function.
This creates a variable scope that the returned function will continue to have access to. This way it always has access to the j variable, and not the i that gets overwritten in the loop.
These inlined functions are abused a bit IMO. It becomes clearer if you simply make it a named function.
for(var i = 0; i < 10; i++) {
// create the new element
a.onclick = createHandler(i);
// append it somewhere
}
function createHandler (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
}
The resulting handler is exactly the same, but the code is much less cryptic.
Right, I'm going to guess that the surrounding code looks like this:
for (var i = 0; i < playitems.length; i++) {
// above code here
}
Now, you could do the obvious thing here, and assign the onclick property like this:
a.onclick = function() {
ytplayer_playitem = i;
ytplayer_playlazy(1000);
};
However that wouldn't work very well, because the value of i changes. Whichever link was clicked, the last one would be the one activated, because the value of i at that point would be the last one in the list.
So you need to prevent this happening. You need to do this by creating a new scope, which is done by creating an extra function, which is immediately invoked:
(function (j) {
// some code here
})(i);
Because i has been passed into the function, the value is passed rather than a reference to the variable being kept. This means that you can now define a function which will have a reference to the correct value. So you get your extra function to return the click handling function:
a.onclick = (function (j) { // j is the right number and always will be
return function () { // this function is the click handler
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
So each a element has its own click handler function, each of which has its own individual j variable, which is the correct number. So the links, when clicked, will perform the function you want them to.
a.onclick = (function (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
This creates a "closure" to ensure that the value of i that is bound to the handler is the value of i "at that time" and not i in the general.
In your code, the function inside the () is an expression, executed and passed the variable i. This is the (i) you see in the end part. In this executed function expression, the i becomes the local variable j. This executed function expression returns the handler function that is to be bound the onclick event carrying the value of j which was i "at that time"
if i did not use the closure:
//suppose i is 1
var i = 1;
a.onclick = function () {
ytplayer_playitem = i;
ytplayer_playlazy(1000);
};
//and changed i
i = 2;
//if you clicked the <a>, it would not be 1 onclick but 2 because you
//did not assign the value of i "at that time". i is "tangible" this way
a.onclick = (function (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
What you have here is a self-invoking anonymous function. Let's break it down, first replacing the body of the function with something simpler (return j + 1;):
function( j ) { return j + 1; }
This s a run-of-the-mill anonymous function or closure. This line of code is an expression, and so it has a value, and that value is a function. Now we could do this:
var foo = function( j ) { return j + 1; }
foo( 5 ); // => 6
You recognize this, I'm sure—we're assigning the anonymous function to the variable foo, and then calling the function by name with the argument i. But, instead of creating a new variable, because the closure is an expression we can call it like this instead:
( function( j ) { return j + 1; } )( 5 ); // => 6
Same result. Now, it's just returning j + 1 but in your code it returns something else: Another anonymous function:
return function() { /* ... */ }
What happens when we have a self-invoking anonymous function that returns a function? The result is the "inner" function that was returned:
a.onclick = ( function( j ) {
return function() {
ytplayer_playitem = j;
ytplayer_playlazy( 1000 );
}
}
)( i );
If i was equal to 9 then a.onclick would now hold a function equivalent to this:
function() {
ytplayer_playitem = 9;
ytplayer_playlazy( 1000 );
}
As others have pointed out, the usefulness of this is that when ( function( j ) { /* ... */ } )( i ) is invoked you are capturing the value of i at that time and putting it into j rather than creating a reference to the value i holds, which may (and probably will) change later on.
This is my code, SetOpacity get invoked with wrong values, why?
function SetOpacity(eID, opacity){
eID.style.opacity = opacity / 100;
eID.style.filter = 'alpha(opacity=' + opacity + ')';
}
function fade(eID, startOpacity, endOpacity){
var timer = 0;
if (startOpacity < endOpacity) {
for (var i = startOpacity; i <= endOpacity; i++) {
setTimeout(function() {SetOpacity(eID, i);}, timer * 30);
timer++;
}
}
}
This should work:
for (var i = startOpacity; i <= endOpacity; i++) {
(function(opacity) {
setTimeout(function() {SetOpacity(eID, opacity);}, timer * 30);
})(i);
timer++;
}
This works as follows:
inside the loop you create an anonymous function (function(...){...}) and immediately call it with a parameter (that's why there are parentheses around function(){}, so you can call it by adding () at the end and passing parameters)
parameters passed to this anonymous function (in this case i, which is opacity inside the function) are local to this anonymous function, so they don't change during the next iterations of the loop, and you can safely pass them to another anonymous function (the first parameter in setTimeout)
Your original version didn't work because:
your function that is passed to setTimeout holds a reference to the variable i (not the value of it), and it resolves its value only when this function is called, which is not at the time of adding it to setTimeout
the value of this variable gets changed in the loop, and before even the first setTimeout executes, i will have reached endOpacity (the last value from the for loop)
Unfortunately JavaScript only has function scope, so it won't work if you create the variable inside the loop and assign a new actual value, because whenever there is some var inside a function, those variables are created at the time of function execution (and are undefined by default). The only (easy) way to create new scope is to create a function (which may be anonymous) and create new variables inside it (parameters are variables too).
This is a closure issue. By the time you run the function, i is already at endOpacity. This will work, by creating another closure:
function SetOpacityTimeout(eID, opacity, timer){
setTimeout(function() {SetOpacity(eID, opacity);}, timer * 30);
}
function fade(eID, startOpacity, endOpacity){
var timer = 0;
if (startOpacity < endOpacity) {
for (var i = startOpacity; i <= endOpacity; i++) {
SetOpacityTimeout(eID,i,timer);
timer++;
}
}
}
Kobi has the right idea on the problem. I suggest you use an interval instead, though.
Here's an example: (Your SetOpacity function remains the same, I left it out here.)
function fade(eID, startOpacity, endOpacity){
var opacity = startOpacity;
SetOpacity(eID, opacity);
var interval = window.setInterval(function(){
opacity++;
SetOpacity(eID, opacity);
// Stop the interval when done
if (opacity === endOpacity)
window.clearInterval(interval);
}, 30);
}
This is am example I used with jquery. "menuitem" is the itemclass and jquery checks the "recentlyOut" class to see if it needs to slide back up.
The code speaks for itself.
$(".menuitem").mouseenter(
function(){
$(this).addClass("over").removeClass("out").removeClass("recentlyOut");
$(this).children(".sub").slideDown();
});
$(".menuitem").mouseleave(
function(){
$(this).addClass("out").addClass("recentlyOut").removeClass("over");
setTimeout(function()
{
var bool = $(".recentlyOut").hasClass("over");
if (!bool)
{
$(".recentlyOut").removeClass("recentlyOut").children(".sub").slideUp();
}
}
, 400);
}
);