在JavaScript或者几乎所有的编程语言中,for循环语句都是处理循环的主要手段,其使用方式相信大家都很熟悉。在处理循环时,我们还可以使用breakcontinue来控制循环的结束或者跳过本次处理。但ES5中引入的forEach作为处理循环的利器能否沿袭for循环的特点呢?答案是否定的,forEach有着自己独特的特性:break、continue不能在forEach中使用

var arr = [1, 2, 3, 4];
arr.forEach(function(item) {
  console.log(item);
  if (item > 2) {
    break;
  }
});
var arr = [1, 2, 3, 4];
arr.forEach(function(item) {
  if (item === 2) {
    continue;
  }
  console.log(item);
});

上面两段代码乍看起来应该像是对的,但是运行时会抛出异常。那我们该如何实现类似的功能呢?

return模拟continue

这里其实有个乍看起来奇特的场景:return语句不能结束forEach。很奇怪吧,即使在switch...case中使用return语句也可以结束一个代码块:

function getColor(color) {
  switch(color) {
    case 'red':
      return '#ff0000';
    case 'blue':
      return '#00ff00';
    default:
      return null;
  }
  console.log('不可知之地'); // A
}

console.log(getColor('red')); // #ff0000

return的作用是用来结束一个代码块的,上面的行A是不会被执行到的。那再来看下returnforEach中的表现:

var arr = [1, 2, 3, 4];
arr.forEach(function(item) {
  console.log(item);
  if (item > 2) {
    return;
  }
});

我们的本意是只打印出1和2,但是实际上结果却是将数据中所有的数据都打印出来了,这是因为我们传入forEach的是一个函数,每次循环都会执行此函数(传入参数不同),而函数中的return只会结束当前函数,所以上面的return没有任何意义,但是我们可以利用此特性起到continue语句的作用:

var arr = [1, 2, 3, 4];
arr.forEach(function(item) {
  if (item > 2) {
    return;
  }
  console.log(item);
});

// 1 2

无法在forEach中模拟break

上面的例子只是调换一下代码位置即可实现只打印出3之前的数据的功能,但是这种实现并不优雅,因为对于2以后的元素来说仍然还要进行非必要的函数调用(假设arr是有序数组),这种方案只适用于跳过特定某些数据的场景:

var arr = [1, 2, 3, 4];
arr.forEach(function(item) {
  // 针对跳过某些数据时可以使用return模拟continue
  if (item === 2) {
    return;
  }
  console.log(item);
});

// 1 3 4

但是对于只打印出3之前的数据的功能来说,使用for循环实现无疑是最好的方案(可以搭配break使用):

var arr = [1, 2, 3, 4];
for (var i=0, len=arr.length; i<len; i++) {
  if (arr[i] > 2) {
    break;
  }
  console.log(arr[i]);
}

// 1 2

我们是无法在forEach中模拟break的,因为术业有专攻,如果想要此功能请大胆的使用for循环。

更多

上文中提到的for循环既包括普通的for循环,其实也包括ES6中的for...of循环(continuebreak能够正常使用)。ES5和ES6之后好多人感觉使用普通的for循环逼格比较低,但是使用普通的for循环并无任何不妥之处,而且对于大数据量来说普通的for循环性能上更具优势