一.構文
作成構文:function* + yield
使用構文:next(returnValue).value/done
P.S.イテレーター と ジェネレーター:function* で定義されるものはイテレーターのジェネレーター(略してジェネレーター)と呼ばれます。これを呼び出すとイテレーターが返されるためです
二.イテレーターの役割
1.関数のスロットリング
時間のかかる複雑なタスクを yield で分割して少しずつ実行します。関数のカリー化 currying とも呼ばれます。詳細は JS 学習ノート 11_ 高級テクニック を参照してください
2.無限シーケンスの生成
例えばフィボナッチ数列など
3.トラバースの容易化
内部状態を手動で管理する必要がありません
三.JavaScript ジェネレーターの例
1.基本的な使い方
function* fun(a) {
yield a + 1;
yield a * 2;
yield a * 2 + 1;
}
var iter = fun(3);
iter.next();
// => Object {value: 4, done: false}
iter.next();
// => Object {value: 6, done: false}
iter.next();
// => Object {value: 7, done: false}
function* fun(a) {
yield a=a + 1;
yield a=a * 2;
yield a=a * 2 + 1;
}
var iter = fun(3);
// => iter.next();
Object {value: 4, done: false}
iter.next();
// => Object {value: 8, done: false}
iter.next();
// => Object {value: 17, done: false}
iter.next();
// => Object {value: undefined, done: true}
関数の実行が yield に遭遇すると、まず yield の後ろの値を return し、関数実行の context を保存します(ブレークポイントを保存するのと似た効果)。次に next() を呼び出すと、context を復元し、yield の次の文から実行を開始し、yield または return に遭遇すると終了します
2.高度な使い方
function* fib() {
var a = 1;
var b = 1;
while (true) {
var current = b;
b = a;
a = a + current;
var reset = yield current;
if (reset) {
a = 1;
b = 1;
}
}
}
var fibSeq = fib();
fibSeq.next();
// => Object {value: 1, done: false}
fibSeq.next();
// => Object {value: 1, done: false}
fibSeq.next();
// => Object {value: 2, done: false}
fibSeq.next();
// => Object {value: 3, done: false}
fibSeq.next();
// => Object {value: 5, done: false}
fibSeq.next(false);
// => Object {value: 8, done: false}
fibSeq.next(true);
// => Object {value: 1, done: false}
fibSeq.next(false);
// => Object {value: 1, done: false}
fibSeq.next(false);
// => Object {value: 2, done: false}
next() はパラメータを受け取ることができ、このパラメータは yield の戻り値として関数に返されます。これにより関数内部の状態を制御できます。例えば上記の reset の有無など
3.少し複雑な例
function* fun(a) {
a = yield a + 1;
a = yield a * 2;
yield a * 2 + 1;
}
var iter = fun(3);
iter.next(0);
// => Object {value: 4, done: false}
iter.next(0);
// => Object {value: 0, done: false}
iter.next(1);
// => Object {value: 3, done: false}
同様のもの:
function* fun(a) {
a = yield a = a + 1;
a = yield a = a * 2;
yield a = a * 2 + 1;
}
var iter = fun(3);
iter.next(0);
// => Object {value: 4, done: false}
iter.next(0);
// => Object {value: 0, done: false}
iter.next(1);
// => Object {value: 3, done: false}
内部実行メカニズムは同じで、書き方が少し複雑なだけです
4.その他
さらに、イテレーターには throw() と return() メソッドがあります。FF は両方実装していますが、Chrome は前者のみ実装しています。詳細は MDN Iterators and generators を参照してください
また、1 つのイテレーターから別のイテレーターを生成する構文もあります。例えば:
function* fun() {
yield 1;
yield 2;
}
var iter = fun();
var newIter = (for (i of iter) i * 2);
newIter.next();
// => Object { value: 2, done: false }
newIter.next();
// => Object { value: 4, done: false }
注意:MDN の例は誤っています。必ず for...of であり、丸括弧でなければなりません(例中の for...in と角括弧は誤りです)
FF はこの構文をサポートしていますが、Chrome はサポートしていません
三.関連構文
1.for...of、for...in、forEach
for...of はプロパティ値をトラバースするために使用され、for...in はプロパティ名をトラバースするために使用されます。forEach は Array.prototype 上のメソッドで、プロパティ名とプロパティ値の両方をトラバースできます
さらに、for...of は DOM NodeList とカスタムイテレーター(function* + yield)もトラバースでき、ES6 の新機能です
例は以下の通り:
var arr = [3, 5, 7];
arr.foo = "hello";
for (var i in arr) {
console.log(i); // logs "0", "1", "2", "foo"
}
for (var i of arr) {
console.log(i); // logs "3", "5", "7"
}
let arr = [3, 5, 7];
arr.foo = "hello";
arr.forEach(function (element, index) {
console.log(element); // logs "3", "5", "7"
console.log(index); // logs "0", "1", "2"
});
2.yield*
yield と機能は似ており、トラバースに使用されますが、yield* の後ろにはイテレーターオブジェクトが続き、後ろのイテレーターに入ってトラバースを完了してから戻るという役割です。例えば:
function* g1() {
yield 2;
yield 3;
yield 4;
}
function* g2() {
yield 1;
yield* g1();
yield 5;
}
var iterator = g2();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
yield* により、イテレーターをネストできるようになり、階層構造を「線形」にトラバースできます(もちろん、すべての下層構造のイテレーターオブジェクトを取得できることが前提ですが)
コメントはまだありません