いまさら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
原始時代
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ループの書き方もとても泥臭い。
とても原始的な書き方なのですが、誰でも、つまり死ぬほど古いブラウザでも理解できるコードという事で、一定の需要はあります。
石器時代
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という変数を使うことによる若干の高速化が図られています。
とても素敵な書き方ですねっ。
もし、原始時代のコードをメンテするという地獄に突き落とされた場合、この石器時代のコードは多少なりとも抗えますので、すこしでも時代を進めることをお薦めします。
中世
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が使えません
というのが、中世までの常識でした。
近代
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では当然のように動かない模様
参考
- https://en.wikipedia.org/wiki/Google_Chrome_version_history
- https://en.wikipedia.org/wiki/History_of_the_Opera_web_browser
- https://en.wikipedia.org/wiki/Microsoft_Edge#Release_history
- https://en.wikipedia.org/wiki/Safari_version_history
- https://en.wikipedia.org/wiki/Firefox_version_history
他にも、Android Browserが対応していない事に目眩と頭痛と吐き気を感じますが、いずれは対応していく事でしょう。
しれっと、classListを使っていますが、これはとても便利なAPIなので、ぜひとも覚えていってください。
IE10以降のブラウザなら、大体サポートしています。
現代
document.querySelectorAll("a").forEach(element => {
element.classList.add("link");
});
現代では、アロー関数が使えるようになっています。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
もう既に常識かもしれませんが、大体のブラウザでもアロー関数は有効です。
アロー関数を使うと、thisを直感的に書けるようになるので、とてもコーディングが楽になります。
var _selft = this;
みたいな古いノウハウを使わなくても良くなるので、アロー関数はどんどん採用して欲しいものです。
という事で、短いコードで振り返るJavaScriptの書き方遷移でした(タイトル変わってるがな)
最後に
最後になりますが、こちらに記載したコードは全てVivaldiで動作確認しています。
Vivaldiでも最新のコードをしれっと動かせたのが楽しかったです。