JavaScript
ES5

2017年はforEach元年

いまさらforEach?

5000兆人のJavaScriptファンの皆様こんばんわ。
JavaScript Advent Calendar 2017 22日目の記事です。
2017年と言えば、forEach元年。
誰が何と言おうと、forEach元年です。
\おめでとう/

茶番はここまでにして、forEachとはなんぞやから書いていきたいと思います。

forEachとは

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
まずは、MDNのドキュメントに目を通しましょう。
配列操作をいい感じにしてくれる機能の事です。ES5で追加された機能ですね。

ブラウザの対応状況

  • Chrome
  • Edge
  • Firefox
  • IE9(?!)
  • Opera
  • Safari

はい。
大抵のブラウザは、forEachに対応しています。
あの悪名高きIEも、9以降からならforEach使えるんですねー。
これは余談なのですが、最近はIE案件を受けることも減ってきていて、いい加減古い書き方を捨てても良いかなーと思うようになってきました。
ってか、最近は古い書き方を捨てて、新しい書き方をどんどん取り入れています。
凄い快適です。

ここまでは前座

長い前振りになりましたが、ここまでは前座です。
ここから、forEachを使う前から、forEachを使った良くある書き方を見つつ、最新版ではどうなっていくのか、ご覧ください。

歴史的レガシィコードで辿るforEach

原始時代

primitive-age.js
var elements = document.getElementsByTagName("a");
for( var i=0; i<elements.length;i++ ){
    var element = elements[i];
    element.className += " link";
}

このコードに意味があるかどうかはともかく、aタグ全てにlinkクラスをくっつけるという非常にシンプルなコードです。
誰が読んでも分かりやすいというメリットがあるのですが、書き方が古すぎますね。
呼び出しているAPIが古いですし、classNameにアクセスしている当たりアレですし、forループの書き方もとても泥臭い。
とても原始的な書き方なのですが、誰でも、つまり死ぬほど古いブラウザでも理解できるコードという事で、一定の需要はあります。

石器時代

stone-age.js
var elements = document.querySelectorAll("a");
for( var i=0,iz = elements.length; i<iz;i++ ){
    var element = elements[i];
    element.className += " link";
}

少しだけ書き方が変わっています。
まず、APIとして、querySelectorAllを使うようになりました。
https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
JavaScriptをある程度かじっている人なら、このAPI使ってHTMLの要素を取得するのは常識ですよね。
余程変なことをしていなければ、IE8でもサポートされているAPIですし、これを使わない手はありません。

forループの書き方もちょっとだけ精錬されています。
個人的にはuupaa式と勝手に呼んでいるのですが、izという変数を使うことによる若干の高速化が図られています。
とても素敵な書き方ですねっ。
もし、原始時代のコードをメンテするという地獄に突き落とされた場合、この石器時代のコードは多少なりとも抗えますので、すこしでも時代を進めることをお薦めします。

中世

middle-age.js
var elements = document.querySelectorAll("a");
var addLinkClass = function( element, index, array ){
    element.className += " link";
};
Array.prototype.forEach.call( elements, addLinkClass );

さあ、ようやく中世に到達しました。
ここでforEachの登場です。
良い書き方ですねー、実にJavaScriptらしい書き方で、僕は好きです。
Arrayオブジェクトのprototypeをcallするというテクニカルな書き方です。
JavaScriptのprototypeベースでのコーディングの面白い所であり、難しい所でもあります。
中世と言いながら、案外ハードルの高い書き方ですが、まあ、こういうテクニックがあるよーって事で、ご理解頂ければと思います。

さて、疑問に思う方が多いかもしれませんが、querySelectorAllで取得したのは配列では無いのかという事です。
elements.lengthみたいにアクセス出来るので、配列の仲間かなーと思いきや、コイツはNodeListという全く別のオブジェクトです。

なので、forEachが使えません

というのが、中世までの常識でした。

近代

modern-times.js
var elements = document.querySelectorAll("a");
var addLinkClass = function( element, index, array ){
    element.classList.add("link");
};
elements.forEach(addLinkClass);

はい!近代に到達し、なんと、NodeListオブジェクトがforEachをサポートするようになりました、拍手ー!
パチパチパチ~

ここが、タイトルに繋がるのですが、多くのブラウザがこのNodeListがforEach対応したのが、2017年なのですっ!
タイトル回収できて良かったです。←なげーよ

https://developer.mozilla.org/ja/docs/Web/API/NodeList/forEach
ここを参考にすれば分かりますが、Edge16でサポートされた事により、ほぼほぼメジャーなブラウザでは、NodeListでforEeachを使うことが可能になっています。
Wikipediaベースになりますが、各ブラウザのリリース履歴を参照すると、サポート時期を推定できます。

  • Chromeは、2016/5/25
  • Operaは、2016/6/8
  • Edgeは、2017/9/26
  • Safariは、2016/9/20
  • Firefoxは、2016/6/8

去年の5月頃から次々と実装されて、Edgeがつい最近サポートしたという形ですね。
なお、IEでは当然のように動かない模様

参考

他にも、Andoroid Browserが対応していない事に目眩と頭痛と吐き気を感じますが、いずれは対応していく事でしょう。

しれっと、classListを使っていますが、これはとても便利なAPIなので、ぜひとも覚えていってください。
IE10以降のブラウザなら、大体サポートしています。

現代

present-day.js
document.querySelectorAll("a").forEach(element => {
   element.classList.add("link");
});

現代では、アロー関数が使えるようになっています。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

もう既に常識かもしれませんが、大体のブラウザでもアロー関数は有効です。
アロー関数を使うと、thisを直感的に書けるようになるので、とてもコーディングが楽になります。

old-technic.js
var _selft = this;

みたいな古いノウハウを使わなくても良くなるので、アロー関数はどんどん採用して欲しいものです。

という事で、短いコードで振り返るJavaScriptの書き方遷移でした(タイトル変わってるがな)

最後に

最後になりますが、こちらに記載したコードは全てVivaldiで動作確認しています。
Vivaldiでも最新のコードをしれっと動かせたのが楽しかったです。