JavaScriptの新しいAPI、ジェネレータの使い方
ES6で新しく追加されたJavaScript APIにジェネレータがあります。使い方次第で新しいJavaScriptの書き方が可能です。ES6 GeneratorはIE11を除くモダンなブラウザではサポートされていますので、ES6が利用できるのであれば使わない手はないでしょう。
ジェネレータの基本形
ジェネレータは function* という形で記述します。そしてyieldという形で値を返却します。このyieldが呼ばれる度にジェネレータの呼び出し元に値が送られます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function* gen(i) { | |
i++; | |
yield i; | |
i++; | |
yield i; | |
i++; | |
yield i; | |
} |
呼び出す際には関数を実行します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const g = gen(3); |
さらに このジェネレータ(g)のnextメソッドを呼び出すとyieldと書かれた部分まで処理が進みます。返り値はオブジェクトで、valueキーに値が、doneキーにyieldが終了したかどうかが入ってきます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} |
nextメソッドではなく、for of を使うこともできます。この場合はvがvalueキーの内容になります。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
for (let v of g) { | |
console.log(v); | |
–> 4、5、6が順番に出力されます | |
} |
オブジェクトを返せば複数の値を渡すこともできます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
nextを使う場合、yieldに対して引数を送ることができます。この値は yield の返り値として受け取れるものになります。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
配列にeachWithIndexを実装する
例えば配列を順番に処理する場合、for を使います。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const ary = []; | |
ary.push(1); | |
ary.push(2); | |
ary.push(3); | |
for (let i = 0; i < ary.length; i += 1) { | |
let value = ary[i]; | |
} |
この毎回 value = ary[i] というのを書くのが面倒です。逆に for of を使った場合にはインデックスがないので不便な時もあります(回避する方法としてentriesを使う手もあります)。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
for (let value of ary) { | |
// インデックスがない | |
} |
そこでeachWithIndexメソッドを作って、インデックスも返ってくるイテレータを作ってみます。これはArrayのprototypeに作ります。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Array.prototype.eachWithIndex = function* () { | |
for (let i = 0; i < this.length; i += 1) { | |
yield {index: i, value: this[i]}; | |
} | |
}; |
後はこのメソッドをfor ofの中で呼び出します。 eachWithIndex は関数であり、それを実行すると処理が開始されるので注意してください。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const ary = []; | |
ary.push(1); | |
ary.push(2); | |
ary.push(3); | |
for (let {index, value} of ary.eachWithIndex()) { | |
console.log(index, value); | |
} |
クラスにイテレーションを追加する
もし自作クラスにイテレーションを追加したい場合には、 Symbol.iterator
を定義します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Hello { | |
constructor() { | |
this.ary = ['a', 'b', 'c']; | |
} | |
* [Symbol.iterator](){ | |
for (let v of this.ary) { | |
yield v + v; | |
} | |
} | |
}; |
これを実行します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const hello = new Hello(); | |
const ary = []; | |
for (let v of hello) { | |
console.log(v); | |
–> aa, bb, ccと出ます | |
} |
こちらはJSFiddleにデモを掲載しています。
イテレーションを使うことで、ループ処理であったり、段階を踏んで処理するようなところが書きやすくなります。JavaScriptはシングルスレッドで非同期処理が面倒ですが、イテレーションを使えば処理が完了してから次の処理に移動すると言ったことも書けるでしょう。
コメントは受け付けていません。