生成的 JavaScript 函数中的变量未按预期运行
Variable in generated JavaScript function doesn't behave as expected
从循环内生成函数时,JavaScript 中的范围界定存在一些问题。
我想要的:
我希望它的工作方式是一个 for
循环,每次迭代都会生成一个名为 doStuff
+ i
的函数。例如,第一次迭代将生成 doStuff1()
,第二次迭代将生成 doStuff2()
,依此类推。函数本身(为了举例)只需要打印出 i
——也就是说,doStuff1()
打印出 1
,doStuff2()
打印出 2
,等等。
我得到的:
实际发生的情况是 i
不在函数中 'stick'。它成为全局范围的一部分或其他东西,所以每个函数都是 10。如果您单击下面代码段中的第一个按钮,您可以看到它。
我尝试过的:
使用生成器函数
在示例二中,我尝试使用 function*
符号来创建合适的函数生成器。我很确定我以前实现过它,但我以前从未使用过这种表示法,所以我可能离题太远了。如果是这样,请告诉我。
结果与示例 2 相同。
使用字符串而不是整数
例如三,我决定尝试使用一个字符串而不是一个整数,它起作用了!对于每次迭代,a
附加到一个字符串,因此当我按顺序 运行 生成的函数时,我得到一个漂亮的字母 a
.
的小金字塔
在不同的地方声明和分配变量
因为我必须在示例 3 中的不同范围内定义 stringOut
变量,所以我决定在示例 4 中对数字进行相同的尝试,并且再次成功!这对我来说没有多大意义,因为在更高的范围内似乎更有可能遇到与示例 1 和 2 相同的问题。
我想知道的:
为什么示例 3 和 4 有效,而示例 1 和 2 无效? 编辑:我的代码被破坏了。 None 有效。
- 我是否正确使用了
function*
生成器声明?
- 执行此操作的最佳(最简单、简洁和可读)方法是什么?我知道做一个函数数组可能是最好的,但在这种情况下这是不可能的。
示例代码:
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
)。在函数被调用时,i
是它退出循环时的值。
var funcs = [];
for (var i=0; i < 5; i++) {
funcs.push(function() {
console.log(i);
});
}
// Here, i is 5
funcs[0](); // 5
funcs[1](); // 5
funcs[4](); // 5
如何解决?通过添加一个额外的闭包,我称该技术为 "freezing a closure"
function createHandler(val) {
return function() {
console.log(val);
}
}
var funcs = [];
for (var i = 0; i < 5; i++) {
// By calling a different function, a copy of i is passed to that function
funcs.push(createHandler(i));
}
// Here, i is 5
funcs[0](); // 0
funcs[1](); // 1
funcs[4](); // 4
您也可以使用 Function.bind
var funcs = [];
for (var i = 0; i < 5; i++) {
// By calling bind, a new function is created, with a new closure
funcs.push(
(function(val) {
console.log(val);
}).bind(null, i)
);
}
// Here, i is 5
funcs[0](); // 0
funcs[1](); // 1
funcs[4](); // 4
从循环内生成函数时,JavaScript 中的范围界定存在一些问题。
我想要的:
我希望它的工作方式是一个 for
循环,每次迭代都会生成一个名为 doStuff
+ i
的函数。例如,第一次迭代将生成 doStuff1()
,第二次迭代将生成 doStuff2()
,依此类推。函数本身(为了举例)只需要打印出 i
——也就是说,doStuff1()
打印出 1
,doStuff2()
打印出 2
,等等。
我得到的:
实际发生的情况是 i
不在函数中 'stick'。它成为全局范围的一部分或其他东西,所以每个函数都是 10。如果您单击下面代码段中的第一个按钮,您可以看到它。
我尝试过的:
使用生成器函数
在示例二中,我尝试使用 function*
符号来创建合适的函数生成器。我很确定我以前实现过它,但我以前从未使用过这种表示法,所以我可能离题太远了。如果是这样,请告诉我。
结果与示例 2 相同。
使用字符串而不是整数
例如三,我决定尝试使用一个字符串而不是一个整数,它起作用了!对于每次迭代,a
附加到一个字符串,因此当我按顺序 运行 生成的函数时,我得到一个漂亮的字母 a
.
在不同的地方声明和分配变量
因为我必须在示例 3 中的不同范围内定义 stringOut
变量,所以我决定在示例 4 中对数字进行相同的尝试,并且再次成功!这对我来说没有多大意义,因为在更高的范围内似乎更有可能遇到与示例 1 和 2 相同的问题。
我想知道的:
为什么示例 3 和 4 有效,而示例 1 和 2 无效?编辑:我的代码被破坏了。 None 有效。- 我是否正确使用了
function*
生成器声明? - 执行此操作的最佳(最简单、简洁和可读)方法是什么?我知道做一个函数数组可能是最好的,但在这种情况下这是不可能的。
示例代码:
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
)。在函数被调用时,i
是它退出循环时的值。
var funcs = [];
for (var i=0; i < 5; i++) {
funcs.push(function() {
console.log(i);
});
}
// Here, i is 5
funcs[0](); // 5
funcs[1](); // 5
funcs[4](); // 5
如何解决?通过添加一个额外的闭包,我称该技术为 "freezing a closure"
function createHandler(val) {
return function() {
console.log(val);
}
}
var funcs = [];
for (var i = 0; i < 5; i++) {
// By calling a different function, a copy of i is passed to that function
funcs.push(createHandler(i));
}
// Here, i is 5
funcs[0](); // 0
funcs[1](); // 1
funcs[4](); // 4
您也可以使用 Function.bind
var funcs = [];
for (var i = 0; i < 5; i++) {
// By calling bind, a new function is created, with a new closure
funcs.push(
(function(val) {
console.log(val);
}).bind(null, i)
);
}
// Here, i is 5
funcs[0](); // 0
funcs[1](); // 1
funcs[4](); // 4