コンテンツへスキップ

JavaScriptの新しいAPI、ジェネレータの使い方

by : 2018/05/09

ES6で新しく追加されたJavaScript APIにジェネレータがあります。使い方次第で新しいJavaScriptの書き方が可能です。ES6 GeneratorはIE11を除くモダンなブラウザではサポートされていますので、ES6が利用できるのであれば使わない手はないでしょう。

ジェネレータの基本形

ジェネレータは function* という形で記述します。そしてyieldという形で値を返却します。このyieldが呼ばれる度にジェネレータの呼び出し元に値が送られます。

function* gen(i) {
i++;
yield i;
i++;
yield i;
i++;
yield i;
}

view raw
index.js
hosted with ❤ by GitHub

呼び出す際には関数を実行します。

const g = gen(3);

view raw
index.js
hosted with ❤ by GitHub

さらに このジェネレータ(g)のnextメソッドを呼び出すとyieldと書かれた部分まで処理が進みます。返り値はオブジェクトで、valueキーに値が、doneキーにyieldが終了したかどうかが入ってきます。

console.log(g.next());
> {done: false, value: 4}
console.log(g.next());
> {done: false, value: 5}
console.log(g.next());
> {done: false, value: 6}
console.log(g.next());
> {done: true, value: undefined}

view raw
index.js
hosted with ❤ by GitHub

nextメソッドではなく、for of を使うこともできます。この場合はvがvalueキーの内容になります。

for (let v of g) {
console.log(v);
> 4、5、6が順番に出力されます
}

view raw
index.js
hosted with ❤ by GitHub

オブジェクトを返せば複数の値を渡すこともできます。

function* gen(i) {
yield {o: i, n: ++i};
yield {o: i, n: ++i};
yield {o: i, n: ++i};
}
let g = gen(3);
for (let {o, n} of g) {
console.log(o, n);
> 3, 4
> 4, 5
> 5, 6
}

view raw
index.js
hosted with ❤ by GitHub

nextを使う場合、yieldに対して引数を送ることができます。この値は yield の返り値として受け取れるものになります。

function* gen(i) {
let m = yield {o: i, n: ++i};
m = yield {o: m, n: ++m};
m = yield {o: m, n: ++m};
}
let g = gen(3);
let {o, n} = g.next().value;
console.log(o, n);
> 3, 4
let {o: a, n: b} = g.next(n).value;
console.log(a, b);
> 4, 5
let {o: c, n: d} = g.next(n).value;
console.log(c, d);
> 5, 6

view raw
index.js
hosted with ❤ by GitHub

配列にeachWithIndexを実装する

例えば配列を順番に処理する場合、for を使います。

const ary = [];
ary.push(1);
ary.push(2);
ary.push(3);
for (let i = 0; i < ary.length; i += 1) {
let value = ary[i];
}

view raw
index.js
hosted with ❤ by GitHub

この毎回 value = ary[i] というのを書くのが面倒です。逆に for of を使った場合にはインデックスがないので不便な時もあります(回避する方法としてentriesを使う手もあります)。

for (let value of ary) {
// インデックスがない
}

view raw
index.js
hosted with ❤ by GitHub

そこでeachWithIndexメソッドを作って、インデックスも返ってくるイテレータを作ってみます。これはArrayのprototypeに作ります。

Array.prototype.eachWithIndex = function* () {
for (let i = 0; i < this.length; i += 1) {
yield {index: i, value: this[i]};
}
};

view raw
index.js
hosted with ❤ by GitHub

後はこのメソッドをfor ofの中で呼び出します。 eachWithIndex は関数であり、それを実行すると処理が開始されるので注意してください。

const ary = [];
ary.push(1);
ary.push(2);
ary.push(3);
for (let {index, value} of ary.eachWithIndex()) {
console.log(index, value);
}

view raw
index.js
hosted with ❤ by GitHub

クラスにイテレーションを追加する

もし自作クラスにイテレーションを追加したい場合には、 Symbol.iterator を定義します。

class Hello {
constructor() {
this.ary = ['a', 'b', 'c'];
}
* [Symbol.iterator](){
for (let v of this.ary) {
yield v + v;
}
}
};

view raw
index.js
hosted with ❤ by GitHub

これを実行します。

const hello = new Hello();
const ary = [];
for (let v of hello) {
console.log(v);
> aa, bb, ccと出ます
}

view raw
index.js
hosted with ❤ by GitHub

こちらはJSFiddleにデモを掲載しています。


イテレーションを使うことで、ループ処理であったり、段階を踏んで処理するようなところが書きやすくなります。JavaScriptはシングルスレッドで非同期処理が面倒ですが、イテレーションを使えば処理が完了してから次の処理に移動すると言ったことも書けるでしょう。

From → HTML5

コメントは受け付けていません。

%d人のブロガーが「いいね」をつけました。