在JavaScript中循环x次的典型方法是:

for (var i = 0; i < x; i++)
  doStuff(i);

但我不想使用++运算符或任何可变变量。那么在ES6中,是否有一种方法来循环x乘以另一种方法?我喜欢Ruby的机制:

x.times do |i|
  do_stuff(i)
end

JavaScript/ES6中有类似的吗?我可以欺骗自己的生成器:

function* times(x) {
  for (var i = 0; i < x; i++)
    yield i;
}

for (var i of times(5)) {
  console.log(i);
}

当然,我仍然在使用i++。至少它在视线之外:),但我希望在ES6中有更好的机制。


当前回答

我认为最好的解决方案是使用let:

for (let i=0; i<100; i++) …

这将为每个主体求值创建一个新的(可变的)i变量,并确保i只在循环语法中的增量表达式中更改,而不是从其他任何地方更改。

我可以作弊,自己做一个发电机。至少i++是看不到的:)

在我看来,这应该足够了。即使在纯语言中,所有的操作(或者至少它们的解释器)都是由使用突变的原语构建的。只要它的作用域是正确的,我看不出这有什么错。

你应该可以接受

function* times(n) {
  for (let i = 0; i < n; i++)
    yield i;
}
for (const i of times(5)) {
  console.log(i);
}

但我不想使用++运算符或任何可变变量。

那么你唯一的选择就是使用递归。你也可以在没有可变i的情况下定义生成器函数:

function* range(i, n) {
  if (i >= n) return;
  yield i;
  return yield* range(i+1, n);
}
times = (n) => range(0, n);

但对我来说,这似乎有点过头了,可能会有性能问题(因为尾调用消除无法用于返回收益*)。

其他回答

处理功能方面:

function times(n, f) {
    var _f = function (f) {
        var i;
        for (i = 0; i < n; i++) {
            f(i);
        }
    };
    return typeof f === 'function' && _f(f) || _f;
}
times(6)(function (v) {
    console.log('in parts: ' + v);
});
times(6, function (v) {
    console.log('complete: ' + v);
});

在函数式范式中,重复通常是一个无限递归函数。要使用它,我们需要惰性求值或延续传递样式。

惰性求值函数重复

const重复= f = > x = > [x,() = >重复(f) (f (x))); Const take = n => ([x, f]) => n === 0 ?X:取(n - 1) (f()); console.log ( 取(8)(repeat(x => x * 2) (1)) // 256 );

我使用一个thunk(一个没有参数的函数)来实现Javascript中的惰性求值。

延续传递样式的函数重复

const重复= f = > x = > [x, k = > k(重复(f) (f (x)))); Const take = n => ([x, k]) => n == 0 ?X: k(取(n - 1)); console.log ( 取(8)(repeat(x => x * 2) (1)) // 256 );

CPS一开始有点吓人。然而,它总是遵循相同的模式:最后一个参数是continuation(一个函数),它调用自己的主体:k => k(…)。请注意CPS将应用程序翻过来,即take(8)(重复…)变成k(take(8))(…)其中k是部分应用的重复。

结论

通过将重复(repeat)与终止条件(take)分离,我们获得了灵活性——将关注点分离到痛苦的尽头:D

在我看来,这个问题最正确的答案(这是有争议的)隐藏在Sasha Kondrashov的评论中,也是最简洁的,只用了两个字:“不”。没有比Ruby的语法更好的for循环替代函数了。我们可能希望有一个,但就是没有。

问题中没有明确说明,但我认为任何“循环N次”问题的解决方案都不应该分配内存,至少不与N成正比。这个标准将排除大多数“原生javascript”的答案。

其他答案显示了Ruby中的实现,这很好,除了这个问题显式地要求本机javascript解决方案。这个问题已经有了一个非常不错的手卷解决方案,可以说是最易读的解决方案之一。

答案:2015年12月9日

就我个人而言,我发现公认的答案既简洁(好)又简洁(坏)。欣赏这个说法可能是主观的,所以请阅读这个答案,看看你是否同意

问题中给出的例子类似Ruby的例子:

x.times do |i|
  do_stuff(i)
end

在JS中使用下面的方法来表达这一点将允许:

times(x)(doStuff(i));

代码如下:

let times = (n) => {
  return (f) => {
    Array(n).fill().map((_, i) => f(i));
  };
};

就是这样!

简单的示例用法:

let cheer = () => console.log('Hip hip hooray!');

times(3)(cheer);

//Hip hip hooray!
//Hip hip hooray!
//Hip hip hooray!

或者,下面是被接受的答案的例子:

let doStuff = (i) => console.log(i, ' hi'),
  once = times(1),
  twice = times(2),
  thrice = times(3);

once(doStuff);
//0 ' hi'

twice(doStuff);
//0 ' hi'
//1 ' hi'

thrice(doStuff);
//0 ' hi'
//1 ' hi'
//2 ' hi'

边注-定义一个范围函数

一个类似的/相关的问题,使用基本非常相似的代码结构,可能是(核心)JavaScript中是否有一个方便的范围函数,类似于下划线的范围函数。

创建一个包含n个数字的数组,从x开始

下划线

_.range(x, x + n)

ES2015

一些替代方案:

Array(n).fill().map((_, i) => x + i)

Array.from(Array(n), (_, i) => x + i)

演示使用n = 10, x = 1:

> Array(10).fill().map((_, i) => i + 1)
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

> Array.from(Array(10), (_, i) => i + 1)
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

在我运行的一个快速测试中,使用我们的解决方案和doStuff函数,上述每个方法都运行了一百万次,前一种方法(Array(n).fill())被证明略快一些。

我还有另一个选择

[...Array(30).keys()]