JavaScript循环的替代方案:repeat(n, function(i) { ... });

6

这是常规的for循环:

for (var i = 0; i < n; i++) { ... }

它用于迭代数组,但也可以重复某个过程n次。

我使用上述形式,但它让我感到厌恶。标题var i = 0; i < n; i++非常丑陋,每次使用时都必须重新编写。

我写这篇文章是因为我想出了一种替代方法:

repeat(n, function(i) { ... });

在这里,我们使用repeat函数,它需要两个参数:
1. 迭代次数
2. 函数体代表正在重复的过程。

"代码后台"看起来像这样:

function repeat(n, f) {
    for (var i = 0; i < n; i++) {
        f(i);
    }
} 

我知道在处理过程中添加两个额外的“级别”会对性能产生影响。

顺便说一句,对于那些使用jQuery库的人,可以通过$.each方法直接实现上述功能,如下所示:

$.each(Array(n), function(i) { ... });  

那么你认为呢?这个repeat函数是原生for循环的有效替代品吗?除了性能问题(我知道这点),还有哪些缺点呢?

原生:

for (var i = 0; i < 10; i++) {
    // do stuff
}

备选方案:

repeat(10, function(i) {
    // do stuff
});

2
随你喜欢吧,兄弟...也许是因为我已经编程25年了,但在我看来,for(..)语法原样使用对我来说完全没问题(而且该语法在许多其他语言的for循环中几乎相同)。 - CrayonViolent
一个不错的优点是 i 变量不会混乱你的作用域。除此之外,我只能说“嗯”。 :) - deceze
我喜欢这个想法:编写你的意图,而不是机器为实现它而做什么。 - Gabriel Glenn
5个回答

10

你说你想要一场革命... 嗯,你知道吗:Ruby在此之前已经实现了它。

Number.prototype.times = function(func) { 
    for(var i = 0; i < Number(this); i++) {
        func(i); 
    }
}

意思是

(50).times(function(i) {
    console.log(i)
})

无论如何,不要与C语言作对,你总是会输的 :-P


5

这是一个有趣的想法,但如果您不喜欢循环的语法,您可以使用其他类型的循环:

var i = arr.length; 
while (i--) {
    // do stuff
}

反向while循环通常比for循环更快。

嗯,这个反向while循环看起来确实更好。 :) - Šime Vidas
特别是如果它写在一行中:var i = n; while (i--) {。我可能会开始使用这个... - Šime Vidas
这相当不错。其他程序员第一次看到时可能需要一点时间来理解,但似乎相当优雅。 - Charles Clayton

2
为了解决其他人提到的没有break语句的问题,我会这样解决:
function repeat(n, f) {
    for (var i = 0; i < n; i++) {
        if (f(i) === false) return;
    }
}

在循环处理程序中返回false将等同于break

另一个缺点是上下文会发生变化。您可能希望将代理上下文的选项添加到循环处理程序中:

function repeat(context, n, f) {
    if (!f) f = n, f = context, context = window;

    for (var i = 0; i < n; i++) {
        if (f.call(context, i) === false) return;
    }
}

现在,一个优点是该函数作用域中的索引被保留,以避免常见的错误:

for (var i = 0; i < 10; i++) {
    setTimeout(function () {
        alert(i); // Will alert "10" every time
    }, 1000);
}

repeat(10, function (i) {
    setTimeout(function() {
        alert(i); // Will alert "0", "1", "2", ...
    }, 1000);
});

你可以通过这样写来节省一些字节:if(f(i)===false)return; - JCOC611
有时候这不是一个错误,而是一个特性。假设你想要查找一个值,如果它没有出现,则将其添加到底部。 - Gonzalo Larralde
哦,还有,正如下面某人提到的:return不能正常工作,但是我认为,使用这段代码,你几乎可以返回调用返回的任何东西,或者如果它是 false 就停止。 - JCOC611

1

看起来相当有效。 我真的认为性能不会下降太多。但是有一个很大的缺点,很容易修复: break 语句。

function repeat(n, f) {
   for (var i = 0; i < n; i++) {
      var tcall=i;
      tcall.die=function(){i=n}
      f.call(tcall);
   }
}  

这样你就可以调用this.die()而不是break;,我认为这会抛出一个错误。


我喜欢.die这个想法,虽然要将它作为单独的参数传递。你会发现,在将die属性分配给原始值后,它会立即丢失。 - David Tang

1
除了你已经提到的主要缺点之外,我认为另一个问题是“return”语句的工作方式会有所不同。(这也是为什么在我的项目中,我经常使用“for”而不是“$.each”的原因。)

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