NodeListをforEachしたいときのパターン

document.querySelectorAllとかで取得したNodeListに対してループ処理をしたいということがよくある。
NodeListはArrayに似ているのでforEachとかを使いたくなるけど、それらは似て非なるものなので無理だ。
単純にforでなら回せるけどそれは嫌なので、他の方法はないかと調べると結構いろんなやり方があった。

まず単純にfor文で回すパターン。

for (var i = 0, len = NodeList.length; i < len; i++) {
  console.log(NodeList[i]);
}

for文で回すラッパー関数を作るパターン。

var each = function (NodeList, iteratee) {
  for (var i = 0, len = NodeList.length; i < len; i++) {
    iteratee(NodeList[i], index, NodeList);
  }
};

each(NodeList, function (el, i) {
  console.log(el, i);
});

forEachのthisを偽装するパターン。

Array.prototype.forEach.call(NodeList, function (node) {
  console.log(node);
});

Arrayに変換するパターン。

Array.prototype.slice.call(NodeList).forEach(function (el) {
  console.log(el);
});

NodeListのprototypeを拡張するパターン(forEachだけ)。

NodeList.prototype.forEach = Array.prototype.forEach;

NodeList.forEach(function (el) {
  console.log(el);
});

NodeListのprototypeを拡張するパターン(全部)。

Object.getOwnPropertyNames(Array.prototype).forEach(function (methodName) {
  if (methodName !== 'length') {
    NodeList.prototype[methodName] = Array.prototype[methodName];
  }
});

Object.keysを使うパターン。

Object.keys(NodeList).forEach(function (key) {
  console.log(NodeList[key]);
});

for...ofで回すパターン。
for...ofはES6の仕様で、Firefoxなら使えるけど他のブラウザはまだ対応してないので、babelをポリフィル付きで使わないといけない

for (let node of NodeList) {
  console.log(node);
}

ちなみにjQueryとlodashの実装を見てみると、どっちも内部的にforを回していた。
個人的には、prototypeを拡張するのは嫌で、sliceでArrayに変換するのも無駄なことをしているような気がするので、forで回すラッパー関数を作ってやるのがきれいだと思う。

追記(11/18)
NodeListにforループをまわすときは、Arrayに変換してからやるほうがパフォーマンスがいいらしい。
NodeListはライブオブジェクトなのでパフォーマンス的に不利、とパーフェクトJavaScriptに書いてた。
実際にchromeで試してみるとだいたい2倍くらい速かったけど、本に書いてるサンプルと処理速度が全然違って(10-100倍くらい)、今の時代そこまで気にしないでいいのかなと思った。
querySelectorとquerySelectorAllというかLive NodeListとStatic NodeList - MOL

参考
NodeList - Web API インターフェイス | MDN
Loop Over querySelectorAll Matches | CSS-Tricks