要旨
- イテレータは分割代入できない。
- 分割代入できるのはイテラブル。
- イテレータはイテラブルとは限らない。
- 自前で実装したイテレータには、
[Symbol.iterator](){return this;}
を生やすこと。
コード
const fib = function fib(){
let n_1 = 1, n_2 = 0;
const iteratorResult ={
value: n_1,
done: false
};
return {
next(){
iteratorResult.value = n_1;
[ n_1, n_2 ] = [ n_1 + n_2, n_1 ];
return iteratorResult;
}
}
}
let f = fib();
let[a, b, c, d, e] = f;
/*
Exception: TypeError: f is not iterable
*/
問題
自前で実装した無限フィボナッチ数列イテレータですが、分割代入をしようとすると
TypeError: f is not iterable
と、イテラブルじゃないよ、と返ってきます。
もちろん、for ... of
構文でも同様のエラーになります。
なぜか。
分割代入できるのはイテレータではなくてイテラブル
iterable (反復可能)プロトコルによって、JavaScript オブジェクトは、for..of構造で値がループしているもの等の反復動作を定義、または、カスタマイズできます。
(略)
iterable であるために、オブジェクトは@@iterator メソッドを実装する必要があります。
反復処理プロトコル - JavaScript | MDN
MDNを見れば明らかだったのですが、分割代入できるのはイテレータではなくてイテラブルであり、イテレータであっても @@iterator
メソッドがなければイテラブルではない、ということです。
ジェネレータはどうなっているのか
const fib = function* fib( n_1 = 0, n_2 = 0 ){
const n = n_1 + n_2 || 1;
yield n;
yield* fib( n, n_1 );
}
let f = fib();
f[Symbol.iterator]() === f;
/*
true
*/
なるほどね。
解決方法
const fib = function fib(){
let n_1 = 1, n_2 = 0;
const iteratorResult ={
value: n_1,
done: false
};
return {
[Symbol.iterator](){ return this; },
next(){
iteratorResult.value = n_1;
[ n_1, n_2 ] = [ n_1 + n_2, n_1 ];
return iteratorResult;
}
}
}
let f = fib();
let[a, b, c, d, e] = f;
e;
/*
5
*/
ということで、自前実装のイテレータには、this
を返す[Symbol.iterator]
メソッドを生やしておきましょう。
↓このMDNの説明は若干不親切かな、と思いました。
イテレーターとジェネレーター - JavaScript | MDN
これを教えてくれた人
@think49 さんです。感謝。
JavaScript - イテレータを進めずに終了しているかどうかを取得する方法はありますか?|teratail