Javascript の for...of

for...in文に対して ES2015で新しく追加された構文にfor...of文がある。 for...ofは、配列などの列挙可能なオブジェクト(NodeList, argumentsなど)、イテレーター/ジェネレーターなどを処理できる。

■ for...ofの構文

for (仮変数 of 列挙可能なオブジェクト) {
    命令
}

見た目はfor...inとそっくりであるが、prototype要素を処理しない、キーでなく値を列挙するなどの違いがある。

// forof.js
let data = ['A', 'B', 'C'];
Array.prototype.foo = {D:'D'};
for (let val of data) {
  console.log(val);
}
$ node forof.js
A
B
C

配列の出力に使ったfor...inのコードをfor...of用に変えただけだが、きちんと対象の配列直属の要素だけが列挙されている。

for...inはDOMのコレクションを扱える

for...inがイテラブル・オブジェクトを扱えるので、コード上に書かれた配列以外にも例えばDOMのコレクションを列挙できる。

例えば次のようなHTML

 <ul>
    <li>item1</li>
    <li>item2</li>
    <li>item3</li>
 </ul>
 let lis = document.getElementsByTagName( 'li' );
 for (val of lis) {
    console.log(val);
 }

ブラウザの開発ツールのコンソールに次のように出力される。

 <li>​item1​</li>​
 <li>​item2​</li>​
 <li>​item3​</li>​

for...inはイテラブル・オブジェクトを扱う

これはHTMLに書かれた<ul>...<li>...タグのDOMが, prototype属性iteratorを持っているからだ。

console.log(lis);

// ブラウザのコンソールに表示された`__proto__`プロパティに`Symbol.iterator`が含まれている
__proto__
..........
Symbol(Symbol.iterator) :
......

iteratorを持つオブジェクトを扱うfor...of文は、Map、Set、String、TypedArray…などのイテラブル・オブジェクトを扱える。

一番シンプルな例は文字列(Stringオブジェクト)。

let str = "Hello";
for (let ch of str) {
  console.log(ch);
}
$ node forof.js
H
e
l
l
o
console.log(str.__proto__);
// Stringオブジェクトもイテレーターを持っている。
String
................
Symbol(Symbol.iterator): ƒ [Symbol.iterator]()
............

for...inSymbol.iteratorを持たない通常のオブジェクトを扱えない

逆に通常のオブジェクトはそのままではイテレータを持っていないので、for.. inで扱えない。

let data = {a: 100, b: 150, c: 200};
for (let val of data) {
  console.log(val);
}
$ node forof.js
...........
for (let val of data) {
                ^
TypeError: data is not iterable
..........

逆に言えば通常のオブジェクトであっても、自分でイテレータを実装できればfor...ofで列挙できるが、ここは難しいのでまた後日まとめることにする。