jsのGenerator関数
5660 ワード
文法的に
まず,Generator関数は状態マシンであり,複数の内部状態をカプセル化していると理解できる.Generator関数を実行するとエルゴードオブジェクトに戻ります.つまり、Generator関数はステータスマシン以外のエルゴードオブジェクト生成関数です.返したエルゴードオブジェクトは、Generator関数内部の各状態を順次巡回することができます.
形式上
Generator関数は普通の関数ですが、二つの特徴があります.
Generator関数の呼び出し方法は普通の関数と同じで、関数名の後に丸括弧を入れます.違いは、Generator関数を呼び出した後、この関数は実行されず、戻ってきたのも関数運転結果ではなく、内部状態を指すポインタオブジェクト、つまり前の章で紹介したエルゴードオブジェクトです.エルゴードオブジェクトのnextメソッドを呼び出して、ポインタを次の状態に移動させます.つまり、nextメソッドを呼び出すたびに、内部ポインタは、関数の先頭または前回停止した場所から、次のyield表現(またはreturn文)に出会うまで実行されます.言い換えれば、Generator関数はセグメントで実行され、yield表現は実行を一時停止したタグであり、next方法は実行を再開することができる.
yield表現
yield表現とreturn文は似ているところもあれば、違いもあります.似ているところは、文の後に続く表式の値を返します.違いは、毎回yieldに会ったら、関数は実行を一時停止し、次はその位置から後に続くが、return文は位置記憶の機能を備えていない.一つの関数では、一回だけ(または一つだけ)return文が実行されますが、複数回(または複数)yield表現が実行されます.通常の関数は1つの値に戻ります.一回だけリセットできます.Generator関数は、任意の複数のyieldがあるので、一連の値を返します.別の観点から見れば、Generatorは一連の値を生成しています.これはその名前の由来です.
文法上の注意点:1.yield表式はGenerator関数の中でしか使用できません.2.yield表式は別の表式の中で使うなら、必ず丸括弧の中に入れてください.3.yield表式は関数パラメータとして使われます.または与えられた表式の右側に置いて、括弧を入れなくてもいいです.たとえば:
yield表現自体には戻り値がありません.つまり、let a=yieldはundefinedに戻ります.nextメソッドは、前のyield表現の戻り値として扱われるパラメータを持つことができます(注意すると、全体の表式の戻り値はyield後方の値だけではなく、let a=yield....パラメータはaの値であり、表式の前の値を上書きします).
Generator.prototype.throw()
Generator関数が返したエルゴードオブジェクトには、関数の外にエラーを投げてGenerator関数の中で捕獲する方法があります.
Generator.prototype.return()
Generator関数は、エルゴードオブジェクトを返します.return方法もあります.与えられた値を返し、Generator関数を巡回します.
yield*表現
文法的な角度から見ると、yield表現の後にエルゴードオブジェクトが付いている場合、yield表現の後に星番号を付ける必要があります.エルゴードオブジェクトが戻ってくることを示します.これはyield表現と呼ばれる(個人的にはyieldは主にエルゴードインターフェースを有するオブジェクトまたは関数として使用される).例えば、あるGenerator関数の中で別のGenerator関数を実行するために使用されます.
オブジェクトの属性がGenerator関数である場合は、以下のように簡単に書くことができます.
Generator関数は常にエルゴードに戻り、ES 6は、このエルゴードがGenerator関数の実例であると規定し、Generator関数のプロトタイプオブジェクト上の方法も継承している.
まず,Generator関数は状態マシンであり,複数の内部状態をカプセル化していると理解できる.Generator関数を実行するとエルゴードオブジェクトに戻ります.つまり、Generator関数はステータスマシン以外のエルゴードオブジェクト生成関数です.返したエルゴードオブジェクトは、Generator関数内部の各状態を順次巡回することができます.
形式上
Generator関数は普通の関数ですが、二つの特徴があります.
,function ;
, yield , (yield “ ”)。
呼び出しGenerator関数の呼び出し方法は普通の関数と同じで、関数名の後に丸括弧を入れます.違いは、Generator関数を呼び出した後、この関数は実行されず、戻ってきたのも関数運転結果ではなく、内部状態を指すポインタオブジェクト、つまり前の章で紹介したエルゴードオブジェクトです.エルゴードオブジェクトのnextメソッドを呼び出して、ポインタを次の状態に移動させます.つまり、nextメソッドを呼び出すたびに、内部ポインタは、関数の先頭または前回停止した場所から、次のyield表現(またはreturn文)に出会うまで実行されます.言い換えれば、Generator関数はセグメントで実行され、yield表現は実行を一時停止したタグであり、next方法は実行を再開することができる.
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
Generator関数を呼び出してエルゴードオブジェクトを返し、Generator関数の内部ポインタを表します.その後、エルゴードオブジェクトのnextメソッドを呼び出すたびに、valueとdoneの2つの属性を持つオブジェクトが返されます.value属性は現在の内部状態の値を表し、yield表現の後の表式の値です.done属性はブール値で、フィニッシュが終了したかどうかを表します.yield表現
yield表現とreturn文は似ているところもあれば、違いもあります.似ているところは、文の後に続く表式の値を返します.違いは、毎回yieldに会ったら、関数は実行を一時停止し、次はその位置から後に続くが、return文は位置記憶の機能を備えていない.一つの関数では、一回だけ(または一つだけ)return文が実行されますが、複数回(または複数)yield表現が実行されます.通常の関数は1つの値に戻ります.一回だけリセットできます.Generator関数は、任意の複数のyieldがあるので、一連の値を返します.別の観点から見れば、Generatorは一連の値を生成しています.これはその名前の由来です.
文法上の注意点:1.yield表式はGenerator関数の中でしか使用できません.2.yield表式は別の表式の中で使うなら、必ず丸括弧の中に入れてください.3.yield表式は関数パラメータとして使われます.または与えられた表式の右側に置いて、括弧を入れなくてもいいです.たとえば:
function* demo() {
foo(yield 'a', yield 'b'); // OK
let input = yield; // OK
}
nextメソッドのパラメータyield表現自体には戻り値がありません.つまり、let a=yieldはundefinedに戻ります.nextメソッドは、前のyield表現の戻り値として扱われるパラメータを持つことができます(注意すると、全体の表式の戻り値はyield後方の値だけではなく、let a=yield....パラメータはaの値であり、表式の前の値を上書きします).
function* f() {
for(var i = 0; true; i++) {
var reset = yield i;
console.log(reset);
if(reset) { i = -1; }
}
}
var g = f();
g.next()
nextメソッドのパラメータは前のyield表現の戻り値を表していますので、nextメソッドを初めて使用した場合、伝達パラメータは無効です.V 8エンジンは直接にnextメソッドを使用した時のパラメータを無視します.nextメソッドを使用したのは2回目からです.パラメータは有効です.意味的には、最初のnext法はエルゴードオブジェクトを起動するために使用されますので、パラメータは必要ありません.Generator.prototype.throw()
Generator関数が返したエルゴードオブジェクトには、関数の外にエラーを投げてGenerator関数の中で捕獲する方法があります.
var g = function* () {
try {
yield;
} catch (e) {
console.log(' ', e);
}
};
var i = g();
i.next();
// :
i.throw('a');
注意点:1.throw方法で投げられたエラーは内部で捕獲されます.前提は少なくとも一回のnext方法を実行しなければなりません.2.throwメソッドが捕獲されたら、次のyield表現が添付されます.つまり、nextを一回実行する方法が付いています.3.Generator関数の外に投げられたエラーは、関数の中で捕獲できます.逆に、Generator関数から投げられたエラーは、関数の外部のcatchに捕獲されます.4.Generatorが実行中にエラーを投げ、内部に捕獲されないと、これ以上実行できなくなります.その後、nextメソッドを呼び出すと、value属性がundefined、done属性がtrueの対象に等しいということになります.すなわち、JavaScriptエンジンは、このGeneratorがすでに実行されていると考えています.Generator.prototype.return()
Generator関数は、エルゴードオブジェクトを返します.return方法もあります.与えられた値を返し、Generator関数を巡回します.
yield*表現
文法的な角度から見ると、yield表現の後にエルゴードオブジェクトが付いている場合、yield表現の後に星番号を付ける必要があります.エルゴードオブジェクトが戻ってくることを示します.これはyield表現と呼ばれる(個人的にはyieldは主にエルゴードインターフェースを有するオブジェクトまたは関数として使用される).例えば、あるGenerator関数の中で別のGenerator関数を実行するために使用されます.
function* foo() {
yield 'a';
yield 'b';
}
//
function* bar() {
yield 'x';
foo();
yield 'y';
}
// yield* foo();
function* bar() {
yield 'x';
yield* foo();
yield 'y';
}
//
function* bar() {
yield 'x';
yield 'a';
yield 'b';
yield 'y';
}
//
function* bar() {
yield 'x';
for (let v of foo()) {
yield v;
}
yield 'y';
}
for (let v of bar()){
console.log(v);
}
プロキシされたGenerator関数にreturn文があれば、プロキシのGenerator関数にデータを返すことができます.function* foo() {
yield 2;
yield 3;
return "foo";
}
function* bar() {
yield 1;
var v = yield* foo();
console.log("v: " + v);
yield 4;
}
var it = bar();
it.next()
// {value: 1, done: false}
it.next()
// {value: 2, done: false}
it.next()
// {value: 3, done: false}
it.next();
// "v: foo"
// {value: 4, done: false}
it.next()
// {value: undefined, done: true}
オブジェクト属性としてのGenerator関数オブジェクトの属性がGenerator関数である場合は、以下のように簡単に書くことができます.
let obj = {
* myGeneratorMethod() {
···
}
};
完全な形式let obj = {
myGeneratorMethod: function* () {
// ···
}
};
Generator関数のthisGenerator関数は常にエルゴードに戻り、ES 6は、このエルゴードがGenerator関数の実例であると規定し、Generator関数のプロトタイプオブジェクト上の方法も継承している.
function* g() {}
g.prototype.hello = function () {
return 'hi!';
};
let obj = g();
obj instanceof g // true
obj.hello() // 'hi!'
上のコードは、Generator関数gが返したエルゴーバーobjがgの例であり、g.prototypeが継承されていることを示している.しかし、gを普通のコンストラクタとして扱うと、効果がありません.gは常にエルゴードオブジェクトであり、thisオブジェクトではありません.function* g() {
this.a = 11;
}
let obj = g();
obj.next();
obj.a // undefined
Generator関数も直接newコマンドと一緒に使用できません.function* F() {
yield this.x = 2;
yield this.y = 3;
}
new F()
融通のきかない方法function* gen() {
this.a = 1;
yield this.b = 2;
yield this.c = 3;
}
function F() {
return gen.call(gen.prototype);
}
var f = new F();
f.next(); // Object {value: 2, done: false}
f.next(); // Object {value: 3, done: false}
f.next(); // Object {value: undefined, done: true}
f.a // 1
f.b // 2
f.c // 3