each
each(iterator[, context]) -> Enumerable
该方法是 Enumerable
的基础。它使我们能够用一种通用的方式来遍历处理所有的元素,并返回
Enumerable
以支持链式调用的编程方式。参数 iterator
是一个函数对象,用于处理
Enumerable
中的每一个元素。
基于 each
进行迭代是 Enumerable 的核心。参数 iterator
指定的函数接受两个参数:
- 迭代中的当前元素
- 起始值为 0 的数字索引,用于表示当前循环的次数。第二个参数不常用(因此在上面的声明中未列出), 但在某些特定的情形下,它是非常有用的。
可选的 context
参数是 iterator
要绑定的对象,若设定该参数,iterator
中的 this
关键字将指向 context
对象。
$break
和 $continue
不再支持 $continue
的使用。该特性在 Prototype 1.5 及以后的版本中将不可用。因为只要在 iterator
指定的函数中简单的使用 return
语句,即可跳出本次循环,从而进入下一次迭代。
在 JavaScript 中,标准的循环可使用 break
和 continue
语句进行短路。然而,使用
iterator
函数时,iterator
的代码在循环的作用域之外:iterator
的代码未运行在当前上下文中。
为提供相同的功能(尽管未做到最佳),Prototype 提供了两个全局的异常对象:$break
和 $continue
。
在循环中抛出它们相当于使用原生的 break
和 continue
语句。这两个异常对象在 each
函数内部能够被正常的捕获到。
样例
['one', 'two', 'three'].each(function(s) { alert(s); });
[ 'hello', 'world'].each(function(s, index) { alert(index + ': ' + s); });
// 先弹出 '0: hello' 然后弹出 '1: world'
// 这比使用 inject 方法来累计值更好,并且更符合我的风格
// 这里...
var result = [];
$R(1,10).each(function(n) {
if(0 == n % 2)
throw $continue;
if (n > 6)
throw $break;
result.push(n);
});
// result -> [1, 3, 5]
// 译注:使用 inject 实现上述功能
$R(1,10).inject([], function(result, n){
if(n < 6 && 0 != n % 2)
result.push(n);
return result;
})
each
vs. _each
如果你曾经读过 Enumerable 的概要文档,可能还记得为了在一个类中混入
Enumerable
,必须提供与类的内部数据结构相匹配的迭代函数。这个最基本的迭代函数必须命名为 _each
,
并且它只接受一个参数:iterator
函数。更进一步的信息请查看概要文档。
事实上,Enumerable.each
封装了 _each
提供的用于实际循环的代码,并且:
- 支持上面所描述的 break/continue 结构。
- 维护和传递相应的 value/index 参数。
优化版本
对于下面所描述的,同时也是一种极为常见的情形,使用一个专门优化的版本可能会更好:
比如说需要在所有元素上调用一个相同的方法,可能会具有参数,无论是否需要获取结果集,使用 invoke 方法会更方便一些。
通常 invoke
比 each
具有更好的执行效率,因为它避免了语法闭包的开销。
然而,它会将结果压入一个数组,这会造成一部分性能的开销,可能你并不希望这么做。但是不管怎样,
你最好在你的实际应用中就上述的情况做一下性能基准测试。