JavaScriptのループについて(配列編)

はい、前回の続きです。

配列の場合

配列のループ方法で調べるとこういう議論もあったりしますが。
JavaScriptで配列をループで処理するベストな書き方は?

まず、for...inは避けましょうっていう傾向はあるみたい。
Effective JavaScriptでも配列の反復処理にはfor...inじゃなくてforを使おうっていう項目があったり。

Effective JavaScript

Effective JavaScript

Effective JavaScriptではfor...inを使用したときにキーと値を混同してしまう可能性について述べていて、for使った方がいいよーって主張してます。

var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var total = 0;
for(var num in numbers) {
  total += num; // 正しくはtotal += numbers[num];
}
console.log(total); // 期待している結果にはなりません

もちろん、プロトタイプ汚染の可能性もあるので、hasOwnPropertyのチェックも入れておかないとですね。
と、まぁ色々面倒なのでfor...inはやめよう。。。


あとは古典的なforループか、ES5のArray.prototype.forEachか。

forはforEachに比べて記述が冗長で、初期化式のvar付け忘れや終了条件の記述ミスなどの恐れがあります。

// iにvarを付け忘れているためグローバル変数になってしまう
for(i = 0; i<length; i++) {...}
// 一回余計な繰り返し
for(var i=0; i<=arrayLength; i++) {...}

けれども、forはbreakを使うことでループの中断が可能。
forEachだとtry-catchで中断するしかない?ので多少見辛くなる感じが。。。

// for使用
function totalUntilZero(array) {
  var result = 0;
  for(var i=0, len=array.length; i<len; i++) {
    if(array[i] === 0) {
      break;
    }
    result += array[i];
  }
  return result;
}
console.log(totalUntilZero([5,8,2,3,0,10,2,9]));

// forEach使用
function totalUntilZeroForEach(array) {
  var result = 0;
  // try-catchでthrowして中断
  try {
    array.forEach(function(num) {
      if(num === 0) {
      	throw result;
  	  }
      result += num;
    });
  } catch(e) {
  	// 特に何もしない
  }
  return result;
}
console.log(totalUntilZeroForEach([5,8,2,3,0,10,2,9]));

もっとも、forEachが使用できるブラウザなら、filterやmap、some、everyなどの反復メソッドも使用できるので、ケースバイケースでそれらを使い分ければ割とスッキリ書けるんですけどね。

function totalUntilZero(array) {
  var result = 0;
  // Array.prototype.everyはコールバックがfalseを返すまで処理を繰り返す
  array.every(function(num) {
    if(num === 0) {
      return false; // break
    }
    result += num;
    return true; // continue
  });
  return result;
}
console.log(totalUntilZero([5,8,2,3,0,10,2,9]));

あれ...書いてみると意外とreturnが多くて、そんなにわかりやすいとは言えない?
まぁいいか。

あと、forEachってインデックス使用できないって思ってたんですけど、コールバックに引数として渡せるっぽいです。
forEach - MSDN
インデックス使うような処理はforで、って思ってたんですけどforEachでもできますね。

結論

シンプルな記述で済むので、forEachとか反復メソッドを使えるところは積極的に使っていければいいのかなと思いました。
ただ、複雑な処理が必要で、ギリギリまでループ内の処理を最適化したいときはforが良いときもあるかもしれないです。
試してないからわからないけども。