javascript关闭立即评估

IT技术 javascript loops closures scope
2021-02-22 21:12:09

考虑以下 Javascript 代码:

var a = [];

var f = function() {

    for (var i = 0; i < 3; i++) {
        a.push(function(){alert(i)});
    }
    for (var j = 0; j < 3; j++) {
        a[j]();
    }
};

警报都打印了 3 次“3”。我想要一个不同的行为 - 在循环的每次迭代中生成一个函数来打印 i 的当前值。即 3 个打印不同索引的函数。

有任何想法吗?

5个回答

创建一个匿名函数,它接受i作为参数并返回该特定函数:

for (var i = 0; i < 3; i++) {
    a.push((function(i) {
        return function() {
            alert(i);
        }
    })(i));
}

for (var j = 0; j < 3; j++) {
    a[j]();
}

或者做一些类似的事情:创建一个匿名函数,它接受i作为参数将函数添加到数组中:

for (var i = 0; i < 3; i++) {
    (function(i) {
        a.push(function() {
            alert(i);
        });
    })(i);
}

for (var j = 0; j < 3; j++) {
    a[j]();
}
这似乎通过提供一种不易受相同潜在缺陷影响的替代解决方案来回避原始问题......您在这里所做的是将值推送到数组上。最初的海报是推送功能,我们认为这些功能将在稍后执行...
2021-04-17 21:12:09
@gnarf,我自己也在争论。我想它确实使意图更清晰。我会把它编辑进去。
2021-04-20 21:12:09
对不起,我评论得太快了。你是对的。另请参阅我对同一问题的类似解决方案的回答。
2021-04-28 21:12:09
不是必需的,但我认为它看起来更清晰,并且更好地描述了将“立即执行”功能包装在()-> 中的意图(function(i){ ... })(i);
2021-04-29 21:12:09
@funka - 你仍然推送函数:创建(function(i){变量范围i以保持函数被调用时的任何内容。有了})(i);你,然后立即调用新的函数与i作为一个参数,它返回一个函数...有效地存储[function(){alert(0);},function(){alert(1);},function(){alert(2);}]a
2021-05-07 21:12:09

只是另一种方法,使用currying

var a = [];
var f = function() {
    for (var i = 0; i < 3; i++) {
        a.push((function(a){alert(a);}).curry(i));
    }
    for (var j = 0; j < 3; j++) {
        a[j]();
    }
};

// curry implementation
Function.prototype.curry = function() {
  var fn = this, args = Array.prototype.slice.call(arguments);
  return function() {
    return fn.apply(this, args.concat(
      Array.prototype.slice.call(arguments)));
  };
};

检查在此处运行的上述代码段

咖喱很好用——虽然现在我饿了……
2021-05-06 21:12:09
var iterate = (function () {
    var i, j = [];
    for (i = 0; i < 3; i += 1) {
        j.push(i);
        alert(j[j.length - 1]);
    }
}());

你不需要闭包仅仅输出一个值。但是,您的代码应该包含在面向对象包含的函数中。无需调用函数即可执行。

@austin:我完全不同意“我强烈反对使用函数而不给它们命名”的说法 - 匿名函数是 javascript ninja 曲目中最有用的工具之一。
2021-04-20 21:12:09
我不同意。函数是一种值类型,就像Javascript 中的42or一样'hello world'无论是赋值给变量还是直接使用都没有什么特别的。有关此行为的示例,请运行:(function(i) { var func = arguments.callee; if(!i) return; console.log('x'); window.setTimeout(function() { func(i - 1); }, 1000); })(4);
2021-04-26 21:12:09
“不必调用函数来执行。” 什么?这种说法不是很清楚,而且听起来是错误的。请说清楚好吗?
2021-05-02 21:12:09
为了执行一个函数,必须发生三件事之一。1) 该函数必须由正在执行的其他东西按名称调用。2) 函数可以插入到方法中,在这种情况下,函数可以是匿名的并且仍然可以执行。我强烈反对使用函数而不给它们命名。3) 函数可以在被解释时完全独立执行,如果它们以右括号后的括号终止,例如 }()。这就是所谓的即时唤起。
2021-05-09 21:12:09
总是命名你的函数是有好处的。1) 匿名函数与立即调用函数的作用完全相同,只是可以在少数地方使用。2)命名函数使文档的编写变得更加容易和更多细节。3) 命名函数为单元测试提供了一个知道位置,其中某些位置在使用匿名函数时不是唯一知道的。因此,始终命名函数有显着的好处,而不命名函数则没有任何好处。
2021-05-12 21:12:09

您可以将循环体放在匿名函数中:

var a = [];

for(var i = 0; i < 3; i++) (function(i) {
    a.push(function() { alert(i); });
})(i)

for(var j = 0; j < 3; j++) {
    a[j]();
}

通过创建该函数并将循环的“i”值作为参数传递,我们在循环体内创建了一个新的“i”变量,该变量基本上隐藏了外部“i”。您推送到数组上的闭包现在可以看到新变量,该变量的值是在第一个循环中调用外部函数函数时设置的。如果我们在创建新变量时使用不同的名称,这可能会更清楚......这做同样的事情:

var a = [];

for(var i = 0; i < 3; i++) (function(iNew) {
    a.push(function() { alert(iNew); });
})(i)

for(var j = 0; j < 3; j++) {
    a[j]();
}

“iNew”的值被分配为 0,然后是 1,然后是 2,因为该函数会被循环立即调用。

功能(一){警报(一)

+1 表示简洁。如果 a.push(function(i){alert(i)}); 用于代替 a.push(function(){alert(i)});
2021-04-25 21:12:09
很有可能,i将是未定义的。
2021-05-03 21:12:09