0
みなさん!こんにちは!
このたび、ライターとして、記事を執筆することになりました!
ネクストライブのかたやんです。
今回は、JavaScriptのジェネレーターの基本について書いていきます。
それでは、さっそく見ていきましょう!
ジェネレーターは、ECMAScript6で導入された、関数ブロック内でコードの実行を停止、再開する機能です。
ジェネレーターは、関数にアスタリスク(*)を付けることで定義できます(ジェネレーター関数)。
ジェネレーター関数は、ジェネレーターオブジェクトというオブジェクトを返します。
それでは、ジェネレーター関数の宣言を確認してみましょう。
//下記いずれもジェネレーター関数の宣言
function* generatorFn() {}
let generatorVar = function* () {}
class generatorClass {
* generatorFn() {}
}
上記のように、ジェネレーター関数を定義すると、ジェネレーター関数内でyieldというキーワードが使用可能になります。
次に、yieldというキーワードを確認してみましょう。
yieldはジェネレーター関数の処理を停止、再開させる機能をもっています。
yieldキーワードに処理が達したところで、処理が停止されnext()
メソッドで処理を再開します。
yieldは、returnのような振る舞いをすると思っていただくと、親しみやすくなるかと思います。
では、実際に、どういう処理の流れになっているのか確認してみましょう。
next()
でyieldまで処理を進めます。yieldがなければ処理が終了となります。
⓵suspendedとは、処理が停止している状態を表しています。
⓶next()
を実行して、処理が終了していなければジェネレーターオブジェクトはdone: false
を返します。
処理が終了していれば、ジェネレーターオブジェクトはdone: true
を返すため、ジェネレーター関数の処理が終了したことが分かります。
⓷next().value
でジェネレーターオブジェクトのvalueだけを抜き出すことができます。
⓸closedとなり、処理が終了したことを表しています。
//ジェネレーター関数の宣言
function* generatorFn() {
let num = 1;
yield num += 1;
num += 1;
yield num += 1;
num += 1;
yield num += 1;
num += 1;
yield num += 1;
num += 1;
return;
}
const generatorVar = generatorFn();
console.log(generatorVar); //generatorFn {<suspended>} ⓵
console.log(generatorVar.next()); //{value: 2, done: false} ⓶
console.log(generatorVar.next().value); //4 ⓷
console.log(generatorVar.next()); //{value: 6, done: false}
console.log(generatorVar.next().value); //8
console.log(generatorVar.next()); //{value: undefined, done: true}
console.log(generatorVar); //generatorFn {<closed>} ⓸
yieldで処理が停止し、next()で処理を再開し、次のyieldまで実行されることが確認できます。
以下のようにnext().value = 10
とすることで、valueの値を変更することができますが、そのあとの処理はnext().value = 10
の影響を受けていません。
console.log(generatorVar); //generatorFn {<suspended>}
console.log(generatorVar.next()); //{value: 2, done: false}
console.log(generatorVar.next().value = 10); //10
console.log(generatorVar.next()); //{value: 6, done: false}
console.log(generatorVar.next().value); //8
console.log(generatorVar.next()); //{value: undefined, done: true}
console.log(generatorVar); //generatorFn {<closed>}
return()
メソッドは、ジェネレーターをclosed状態にします。
⓵まだ、yieldが存在しているのにdone: true
になっていることが分かります。
//ジェネレーター関数の宣言
function* generatorFn() {
let num = 1;
yield num += 1; // num == 2
yield num += 1; // num == 3
}
const generatorVar = generatorFn();
console.log(generatorVar); //generatorFn {<suspended>}
console.log(generatorVar.return()); //{value: undefined, done: true} ⓵
console.log(generatorVar); //generatorFn {<closed>}
returnの引数に値を入れると、valueにその引数が組み込まれます。
console.log(generatorVar.return(3)); //{value: 3, done: true}
throw()
メソッドは、ジェネレーターオブジェクトにエラーを注入し、ジェネレーターをclosed状態にします。
//ジェネレーター関数の宣言
function* generatorFn() {
let num = 1;
yield num += 1; // num == 2
}
const generatorVar = generatorFn();
console.log(generatorVar); //generatorFn {<suspended>}
try {
generatorVar.throw('errorが発生しました');
} catch (e) {
console.log(e); //errorが発生しました
}
console.log(generatorVar); //generatorFn {<closed>}
yieldにアスタリスクを付けることで、イテラブルな処理が可能になります。
ジェネレーターを用いて配列の中身を一つ一つ取り出す場合、以下のような記述が考えられます。
function* generatorFn() {
for (let i of [1, 2, 3]) {
yield i;
}
}
for (let g of generatorFn()) {
console.log(g);
//1
//2
//3
}
しかし、
⓵yield*キーワードを使用することで、以下のように簡単に記述することができます。
⓶yieldにアスタリスクを付けないと、イテラブルではなくなります。
//⓵
function* generatorFn() {
yield* [1, 2, 3];
}
for (let i of generatorFn()) {
console.log(i);
// 1
// 2
// 3
}
//----------------------------------------------------------------
//⓶
function* generatorFn2() {
//アスタリスクなし
yield [1, 2, 3];
}
for (let i of generatorFn2()) {
console.log(i);
//(3) [1, 2, 3]
}
ジェネレーターは、
⓵ジェネレーター関数の処理を停止させるyieldキーワードに対応している。
⓶ジェネレーターオブジェクトを生成する。
⓷イテラブルな処理を少ないコードで実行できる。
⓸ジェネレーターはECMAScript6で導入された。
ということを紹介しました。
⓸からも分かるように、ジェネレーターは比較的新しい機能です。
そのため、Babelのような新しいJavaScriptのコードを古いバージョンに変換する機能(トランスパイル)等を使用する場合、不具合が発生する恐れがあるため注意が必要です。
ジェネレーターは、ちょっと変わったアプローチかもしれませんが、
この記事が、少しでも、あなたの役に立つことができれば幸いです。