現代的JavaScriptおれおれアドベントカレンダー2017 – 22日目

概要

配列とか Map とかは for-of 文を使って、添え字アクセス? [] を使わずにぐるぐるできます。

const arr = ['Hello', 'World', '!'];

for (let item of arr) {
  console.log(item);
}

普通のオブジェクトでは使えないけど、インターフェイスを追加すれば使えるようにもなります。

使い方

普通の for 文を使って配列で arr[i] する代わりに for-of でいきなり値を取ってこれます。

const arr = ['Hello', 'World', '!'];

for (let item of arr) {
  console.log(item);
}

配列以外でも反復可能なオブジェクト(後述)で使えます。

const map = new Map([[1, 11], [2, 22], [3, 33]]);

for (let [key, value] of map) {
  console.log(`${key}: ${value}`);
}

文字列と絵文字

文字列でも使えます。絵文字とかのサロゲートペアを上手に扱ってくれます。やったね。

const str = '#sushi🍣';

console.log('--- for ---');
for (let i = 0; i < str.length; i++) {
  console.log(str[i]);
}

console.log('--- for-of ---');
for (let item of str) {
  console.log(item);
}

--- for ---
#
s
u
s
h
i
�
�
--- for-of ---
#
s
u
s
h
i
🍣

反復可能なオブジェクト

for-of 文は反復可能なオブジェクトに対して利用することができます。

対応してないオブジェクトだとエラーに。

const obj = {};

for (let item of obj) {  // TypeError: obj[Symbol.iterator] is not a function
    console.log(item);
}

反復可能なオブジェクト

この「反復可能(イテラブル)なオブジェクト (iterable object)」とは、「イテレータ」を作成するインターフェイスを整えたオブジェクトのことです。配列 Array や Map 等が該当します。

インターフェイスが整っていれば何でも良いので、自作することもできます。

// 反復可能なオブジェクト
const iterable = {
  // for-ofに必要なインターフェイス
  [Symbol.iterator]() {
    const max = 10;
    let n = 0;

    const iterator = {
      next() {
        return { value: n++, done: n > max };
      },
    };

    return iterator;
  },
};

// よっしゃー使うぞー
for (let item of iterable) {
  console.log(item);
}

詳細は別稿参照。

その他

for-in と for-of

もちろん別物なんだけど、同じ項 (13.7.5) でまとめて定義されてるので、兄弟みたいなものらしい。

for-in はカンマ , 区切りの値を置ける

なんでOKなんだろ。

const arr1 = [100];
const arr2 = [200];

arr1.foo = 123;
arr2.bar = 123;
for (let index in arr1, arr2) {
  console.log(index);
}

もちろん末尾のもの arr2 が有効になります。

0
bar

for-of でやると構文エラーです。

const arr1 = [100];
const arr2 = [200];

for (let item of arr1, arr2) {  // SyntaxError: Unexpected token ,
  console.log(item);
}

for-in で初期値を書ける場面が

"use strict" の厳格モードでない場面で、 var をここで使用する場合のみ、初期値を置ける。 let を使ったり、別の場所で var 宣言してからの代入だとだめ。

const arr = [1, 2, 3];

for (var index = 'wow' in arr) {
  console.log(index);
}

といってもその初期値が適用される場面てあるんだろか。

謎仕様だなー。後方互換のためなのかな。

参考

  • ECMAScript® 2017 Language Specification
    • 13.7.5 The for-in and for-of Statements
    • 13.7.5.12 Runtime Semantics: ForIn/OfHeadEvaluation ( TDZnames, expr, iterationKind ) … イテレータの作成。初期処理的な
    • 13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation ( lhs, stmt, iterator, iterationKind, lhsKind, labelSet ) … イテレータを使って反復処理する
    • 13.7.5.15 EnumerateObjectProperties ( O ) … for-in 用のイテレータ
    • 6.1.5.1 Well-Known Symbols … Symbole.iterator
    • 7.4.1 GetIterator ( obj [ , method ] )
    • 7.4.5 IteratorStep ( iterator ) … イテレータで反復
    • 25.1 Iteration
    • B.3.6 Initializers in ForIn Statement Heads … for-in で初期値使える
  • for…of – JavaScript | MDN
  • for…in – JavaScript | MDN