JY-CONTENTS

JY

JY-CONTENTS
search

+

MENU

【Javascript】ジェネレータの仕組みをしっかりと理解する

【Javascript】ジェネレータの仕組みをしっかりと理解する

(DATE)

-

2021.01.25

(CATEGORY)

-

「function*」と関数を宣言する事でその関数はジェネレーター関数になります。それ以外は普通の関数と同じ構文を使います。
ジェネレータを呼び出すときにはすぐには実行されず、まずは Generator オブジェクトが返されます。そのあとで、イテレータのnext メソッドを呼び出すたびに実行が進みます。

// ジェネレータ定義の関数
function* num() {
    yield 1; // ①
    yield 2; // ②
    yield 3; // ③
}

// 呼び出してもすぐには実行されない
console.log(num()) // Object [Generator] {}

const it = num() // ジェネレータオブジェクト取得

console.log(it.next()) // { value: 1, done: false }
console.log(it.next()) // { value: 2, done: false }
console.log(it.next()) // { value: 3, done: false }
console.log(it.next()) // { value: undefined, done: true }

ジェネレータの場合、呼び出し側に値を供給するためには yield キーワードが使われます。
yield キーワードで返される値は、イテレータの next メソッドでイテレータオブジェクトの値として取得できます。
returnも使われますが、通常は値を返すためには使われません。

動きとしては、

  1. 最初の it.next() 実行で①の yield までが実行され、関数は一旦停止します。
  2. 二番目の it.next() 実行で 1. の停止位置から②の yield までが実行され、関数は一旦停止します。
  3. 三番目の it.next() 実行で 2. の停止位置から③の yield までが実行され、関数は一旦停止します。
  4. 四番目の it.next() 実行ではもう返す値はないので value は undefined、done は true となり関数の実行が終了します。

反復可能プロトコルと反復子プロトコルについては以下の記事をご参考ください。
【javascript】イテレータの仕組みをしっかりと理解する

反復子プロトコル(イテレータ)だけでなく、反復可能プロトコルにも準拠しているので、for…ofでの処理も可能です。

function* num() {
    yield 1;
    yield 2;
    yield 3;
}

for (let nums of num()) { 
    console.log(nums); 
    // 1
    // 2
    // 3
}

無限イテレータ

以下はジェネレーター関数によって無限に Generator オブジェクト(イテレータ)を返すサンプルです。

// ジェネレータ定義の関数
function* sam() {
    let a = 0

    while (true) {
        yield a++;
    }
}

// 呼び出してもすぐには実行されない
console.log(sam()) // Object [Generator] {}

// この段階ではジェネレータオブジェクトが返される
let gene = sam()

// 変数geneにはジェネレータオブジェクトが入っているのでnextメソッドが使える
console.log(gene.next()) // { value: 0, done: false }
console.log(gene.next()) // { value: 1, done: false }
console.log(gene.next()) // { value: 2, done: false }
// 無限に続く

yield式(ジェネレータに値を渡す)

式は評価をすると結果として値をもちます。yield は式なので、評価の結果、何らかの値になることになります。どんな値になるかというと、next メソッドを呼出す時に渡す引数の値になります。
next メソッドの引数の値は、直前の yield と置き換えられたように渡されます。
1回目の next メソッド呼出し時は、引数に値を渡しても渡さなくても無視されます。

function* sam() {
    // yield式は評価の結果、nextの引数の値を値として持つ

    // (1回目のnext呼出し): 引数に値を渡しても渡さなくてもyieldの値は無し(undefined)
    // 1回目はnextの引数の値は必ず無視される
    const name = yield "名前";

    // (2回目のnext呼出し): 引数には「山田」の文字列が渡され、これが直前のyieldの値になる
    // ので定数nameには「山田」が入る
    const address = yield "住所";

    // (3回目のnext呼出し): 引数には「東京都」の文字列が渡され、これが直前のyieldの値になる
    // ので定数addressには「東京都」が入る
    yield `名前:${name}、住所:${address}`;
}


let it = sam() // イテレータが返る
console.log(it.next()) // { value: '名前', done: false }
console.log(it.next("山田")) // { value: '住所', done: false }
console.log(it.next("東京都")) // { value: '名前:山田、住所:東京都', done: false }

yield*

yieldを「yield*」にすることで配列のような反復可能なオブジェクトを以下のように処理できます。

function* sam(){
    yield* [1, 2, 3];
    yield* [4, 5, 6];
}

var it = sam();

console.log( it.next() ); // { value: 1, done: false }
console.log( it.next() ); // { value: 2, done: false }
console.log( it.next() ); // { value: 3, done: false }
console.log( it.next() ); // { value: 4, done: false }
console.log( it.next() ); // { value: 5, done: false }
console.log( it.next() ); // { value: 6, done: false }
console.log( it.next() ); // { value: undefined, done: true }


// ループでの処理
for (var v of it) console.log(v);
// 1
// 2
// 3
// 4
// 5
// 6

return

ジェネレータのどこかでreturnを呼び出しすとdoneがtrueになり、valueプロパティはreturnに指定した値になります。
return 実行後は関数は終了しますので、yield があっても実行されません。

function* sam() {
    yield 1;
    yield 2;
    return 3;
    yield 4; // 実行されない
}

const it = sam();
console.log(it.next()); // { value: 1, done: false } 
console.log(it.next()); // { value: 2, done: false } 

// return実行。doneはtrueになる
console.log(it.next()); // { value: 3, done: true }

// return以降のyieldは実行されない
console.log(it.next()); // { value: undefined, done: true }

for… of で使った場合、done が true になっている事から 3 が表示されません。

function* sam() {
    yield 1;
    yield 2;
    return 3;
}

const it = sam();

for (var v of it) console.log(v);
// 1
// 2

return はジェネレータを早期に終了してしまう場合にのみ用いるべきです。ジェネレータから return を呼び出すときには、通常は値を指定しないほうがよいでしょう。

NEW TOPICS

/ ニュー & アップデート

SEE ALSO

/ 似た記事を見る

JY CONTENTS UNIQUE BLOG

search-menu search-menu