jsのGenerator関数

5660 ワード

文法的に
まず,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関数のthis
Generator関数は常にエルゴードに戻り、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