ES6(ECMAScript 6)中是否有一种不使用可变变量来循环x次的机制?

256

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

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

但是我不想使用 ++ 操作符或任何可变变量。那么在 ES6 中,有没有其他的方法可以循环 x 次?我喜欢 Ruby 的机制:

但是我不想使用 ++ 操作符或任何可变变量。那么在 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中有一个更好的机制。

4
可变的循环控制变量为什么是个问题?只是一个原则吗? - doldt
3
@doldt - 我正在试图教授JavaScript,但我正试验将可变变量的概念延迟到更晚的阶段。 - at.
6
我们离题有些远了,但是在学习可变变量之前,您确定转向ES6生成器(或任何其他新的、高级的概念)是一个好主意吗? :) - doldt
7
@doldt - 或许吧,我正在尝试。采用函数式编程的方法处理 JavaScript。 - at.
1
使用let关键字在循环中声明变量,其作用域仅限于循环内部。 - ncmathsadist
24个回答

7

虽然我不会教授这种方法(或在我的代码中使用),但这是一种无需改变变量且不需要ES6的codegolf-worthy解决方案:

Array.apply(null, {length: 10}).forEach(function(_, i){
    doStuff(i);
})

实际上这更像是一个有趣的概念验证,而不是一个有用的答案。


Array.apply(null, {length: 10})不能直接用Array(10)吗? - Pavlo
1
@Pavlo,实际上不是这样的。Array(10)将创建一个长度为10的数组,但其中没有定义任何键,这使得forEach结构在此情况下无法使用。但如果您不使用forEach,则确实可以简化,参见zerkms的答案(尽管使用了ES6!)。 - doldt
有创意,@doldt,但我正在寻找可教和简单的东西。 - at.

4

如果你愿意使用一个库,也可以使用 lodash 的 _.times 或者 underscore 的 _.times

_.times(x, i => {
   return doStuff(i)
})

请注意,这将返回一个结果数组,因此更像是这个Ruby代码:
x.times.map { |i|
  doStuff(i)
}

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

惰性求值的函数重复

const repeat = f => x => [x, () => repeat(f) (f(x))];
const take = n => ([x, f]) => n === 0 ? x : take(n - 1) (f());

console.log(
  take(8) (repeat(x => x * 2) (1)) // 256
);

我使用thunk(一个没有参数的函数)在Javascript中实现了延迟求值。

使用续传风格进行函数重复

const repeat = f => x => [x, k => k(repeat(f) (f(x)))];
const take = n => ([x, k]) => n === 0 ? x : k(take(n - 1));

console.log(
  take(8) (repeat(x => x * 2) (1)) // 256
);

CPS 刚开始可能会有点吓人。然而,它总是遵循相同的模式:最后一个参数是 continuation(一个函数),它调用自己的体:k => k(...)。请注意,CPS 将应用程序反转,即 take(8) (repeat...) 变成了 k(take(8)) (...),其中 k 是部分应用的 repeat
结论
通过将重复(repeat)与终止条件(take)分离,我们获得了灵活性 - 关注点的划分到苦尽甘来 :D

3
据我所知,ES6中没有类似于Ruby的times方法的机制。但是你可以通过使用递归来避免突变:
let times = (i, cb, l = i) => {
  if (i === 0) return;

  cb(l - i);
  times(i - 1, cb, l);
}

times(5, i => doStuff(i));

示例: http://jsbin.com/koyecovano/1/edit?js,console

(注:该示例为英文,需要自行翻译)

1
我能想到的最简单的创建列表/数组的方法是在范围内使用以下代码: Array.from(Array(max-min+1), (_, index) => index+min)

1

此解决方案的优点

  • 最容易阅读/使用(个人认为)
  • 返回值可用作总和,也可以忽略不计
  • 纯es6版本,还有TypeScript版本的链接

缺点 - 变异。只是内部使用,我不在意,但可能有其他人会关心。

示例和代码

times(5, 3)                       // 15    (3+3+3+3+3)

times(5, (i) => Math.pow(2,i) )   // 31    (1+2+4+8+16)

times(5, '<br/>')                 // <br/><br/><br/><br/><br/>

times(3, (i, count) => {          // name[0], name[1], name[2]
    let n = 'name[' + i + ']'
    if (i < count-1)
        n += ', '
    return n
})

function times(count, callbackOrScalar) {
    let type = typeof callbackOrScalar
    let sum
    if (type === 'number') sum = 0
    else if (type === 'string') sum = ''

    for (let j = 0; j < count; j++) {
        if (type === 'function') {
            const callback = callbackOrScalar
            const result = callback(j, count)
            if (typeof result === 'number' || typeof result === 'string')
                sum = sum === undefined ? result : sum + result
        }
        else if (type === 'number' || type === 'string') {
            const scalar = callbackOrScalar
            sum = sum === undefined ? scalar : sum + scalar
        }
    }
    return sum
}

TypeScript版本
https://codepen.io/whitneyland/pen/aVjaaE?editors=0011

1
我有另一种选择。
[...Array(30).keys()]

5年晚了:https://stackoverflow.com/a/43789388/215552 - undefined

1

我只是想把这个放在这里。如果你正在寻找一个紧凑的函数,而不使用数组,并且对可变性/不可变性没有问题:

var g =x=>{/*your code goes here*/x-1>0?g(x-1):null};
 

0

在我看来,这个问题最正确的答案(有争议)似乎被Sasha Kondrashov的评论埋没了,而且也是最简洁的,只使用了两个字符:“no”。没有任何功能上的替代方案可以像Ruby语法那样好用。我们可能希望有一个,但实际上并没有。

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

其他答案展示了像Ruby中的实现一样的解决方案,这很好,但问题明确要求一个本地的JavaScript解决方案。而且问题中已经有一个非常不错的手动解决方案,可以说是所有解决方案中最易读的之一。


0

For me, this is the easiest answer to understand for many levels of developers

const times = (n, callback) => {
    while (n) {
        callback();
        n--;
    }
}

times(10, ()=> console.log('hello'))


在 times 函数内部有没有一种方法可以获取 n 的值? - Kelsey Hannan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接