LoginSignup
13
6

More than 5 years have passed since last update.

JavaScriptのentries()を多重ループ内で使う場合の注意

Last updated at Posted at 2017-07-25

ES2015のArray.prototype.entries()を多重ループ内で使うケースがあったのですが、
entries()を書く場所によって意図せぬ挙動になってハマったのでメモ。

例えばこんなコードがあったとします。
(この場合entries()は使う必要ないんですが、説明のためなのでツッコミなしで)

const func = (arr) => {
  for (let i = 0; i < 2; i++) {
    for (const val of arr.entries()) {
      console.log(`${i}, [${val}]`);
    }
  }
};
const arr = [1, 2, 3];
func(arr);
0, [0,1]
0, [1,2]
0, [2,3]
1, [0,1]
1, [1,2]
1, [2,3]

これを、関数呼び出しの時点でentries()使うように書き換えると、外側ループが1回しか回らなくなっちゃいました!

const func = (arr) => {
  for (let i = 0; i < 2; i++) {
    for (const val of arr) {
      console.log(`${i}, [${val}]`);
    }
  }
};
const arr = [1, 2, 3];
func(arr.entries());
0, [0,1]
0, [1,2]
0, [2,3]

理由ですが、正確には「外側ループが1回しか回っていない」のではなく、「2回目は内側ループが回らない」ということなのす。
entries()を外側ループより前に書いた場合、「作成されたイテレータが回り切って元に戻らない」のでした。

試しにデバッグ文を追加すると、こんな出力になります。

const func = (arr) => {
  for (let i = 0; i < 2; i++) {
    console.log(`外側ループ ${i}`);
    for (const val of arr) {
      console.log(`${i}, [${val}]`);
    }
  }
  console.log('ループ終了');
};
const arr = [1, 2, 3];
func(arr.entries());
外側ループ 0
0, [0,1]
0, [1,2]
0, [2,3]
外側ループ 1
ループ終了

なので、関数の中にentries()書いても↓じゃダメ。

const func = (arr) => {
  const itr = arr.entries();
  for (let i = 0; i < 2; i++) {
    for (const val of itr) {
      console.log(`${i}, [${val}]`);
    }
  }
};
const arr = [1, 2, 3];
func(arr);

↓ならOKですね。
外側ループが1周する毎にentries()が呼ばれてイテレータが再作成されるので。

const func = (arr) => {
  for (let i = 0; i < 2; i++) {
    const itr = arr.entries();
    for (const val of itr) {
      console.log(`${i}, [${val}]`);
    }
  }
};
const arr = [1, 2, 3];
func(arr);

たぶんイテレータの扱いとしては基本的なことなんでしょうけど、
初心者は「何で変数の宣言場所違うと挙動変わるんじゃー(#゚Д゚)!」ってなりそうなので、
記事に残しておきました。

ではまた〜。

13
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
6