I'm having some issues with scoping in JavaScript when generating a function from within a loop.
What I Want:
The way I want this to work is a for
loop that for each iteration, generates a function named doStuff
+ i
. For example, the first iteration will generate doStuff1()
the second will generate doStuff2()
and so on and so forth. The functions themselves (for the sake of example) just need to print out i
—that is, doStuff1()
prints 1
, doStuff2()
prints 2
, etc.
What I Get:
What's actually happening, is i
doesn't 'stick' in the function. It becomes part of the global scope or something so it's 10 for every function. You can see this if you click the first button in the snippet below.
What I've Tried:
Using a Generator Function
In example two, I've tried using the function*
notation to create a proper function generator. I'm pretty sure I implemented it before, but I've never used that notation before so I could be way off. Please let me know if that's the case.
The result of this is the same as example 2.
Using a String Instead of an Integer
For example three, I decided to try using a string instead of an integer and it works! For every iteration, a
is appended to a string so when I run the generated functions in order, I get a nice little pyramid of the letter a
.
Declaring and Assigning Variables in a Different Place
Since I had to define the stringOut
variable in a different scope in example 3, I decided to try the same with numbers for example 4, and it worked again! This doesn't make much sense to me since being in a higher scope seems like it would be more likely to suffer from the same problems as example 1 & 2.
What I Want to Know:
How come examples 3 & 4 work, while 1 & 2 don't?Edit: My code was broken. None of it works.- Did I use the
function*
generator declaration properly? - What is the best (most simple, concise, and readable) way to do this? I know making an array of functions would probably be best, but in this situation that isn't possible.
Example Code:
function test1() {
document.getElementById("output").innerHTML = "#1 Output:";
var myFunctions = [];
for (var i = 0; i < 10; i++) {
myFunctions[i] = function() {
document.getElementById("output").innerHTML += "<br>" + i;
}
}
for (var j = 0; j < 10; j++) {
myFunctions[j]();
}
}
function test2() {
document.getElementById("output").innerHTML = "#2 Output:";
window.test2Funcs = {};
function* Generator() {
var functionName = "doStuff";
var number = 0;
while (number < 10) {
number++;
yield {
myFunction: function() {
document.getElementById("output").innerHTML += "<br>" + number;
},
name: functionName + (number)
}
}
}
var generator = new Generator();
for (var k = 0; k < 10; k++) {
var out = generator.next().value;
window.test2Funcs[out.name] = out.myFunction;
}
for (var l = 1; l < 11; l++) {
func = "doStuff" + l;
test2Funcs[func]();
}
}
function test3() {
document.getElementById("output").innerHTML = "#3 Output:";
var myFunctions = [];
var stringOut = "";
for (var i = 0; i < 10; i++) {
stringOut += "a"; // Edit. Moved from within function below.
myFunctions[i] = function() {
document.getElementById("output").innerHTML += "<br>" + stringOut;
}
}
for (var j = 0; j < 10; j++) {
myFunctions[j]();
}
}
function test4() {
document.getElementById("output").innerHTML = "#4 Output:";
var myFunctions = [];
var numOut = 0; // Edit. Used to be var numOut = "";
for (var i = 0; i < 10; i++) {
numOut++; // Edit. Moved from within function below.
myFunctions[i] = function() {
document.getElementById("output").innerHTML += "<br>" + numOut;
}
}
for (var j = 0; j < 10; j++) {
myFunctions[j]();
}
}
document.getElementById("test1").addEventListener("click", test1);
document.getElementById("test2").addEventListener("click", test2);
document.getElementById("test3").addEventListener("click", test3);
document.getElementById("test4").addEventListener("click", test4);
<button id="test1">1st Attempt</button>
<button id="test2">2nd Attempt</button>
<button id="test3">3rd Attempt</button>
<button id="test4">4th Attempt</button>
<div id="output">
Output:
</div>
i
referenced inside each constructed function is thei
external to those functions in the scope of thetest1
function itself. They all refer to the same variablei
, and it's value is the same when the functions are all run. – Cassatti
. which is bounded to function scope. – Toothed