C#開発者のための最新JavaScript事情(ジェネレータ関数編):特集:C#×JavaScript(3/4 ページ)
ECMAScript 2015のジェネレータ関数とyield式を使うと、C#の反復子ブロックとyield return文と似た形ですっきりと反復処理を記述できる。
for-of文
for-of文もES2015で導入された構文だ。これはイテレート可能なオブジェクト(コレクション)を対象として、反復処理を行うために使用する。イテレート可能なオブジェクトとは、先ほども出てきたSymbol.iteratorプロパティを持つオブジェクトのことであり(SymbolもES2015で導入された機能だが、ここでは詳細は割愛する)、ジェネレータ関数以外にも配列や文字列などがこれに該当する。
以下に例を示す。
var array1 = [ "foo", "bar", "baz"];
array1.hoge = "hogehoge";
var obj = { name: "foo", tel: "xxxx" };
// hogeプロパティまで列挙される。また、要素にはインデックス指定でアクセスする
for (var i in array1) {
console.log(i + ": " + array1[i]);
}
for (var i in obj) {
console.log(i + ": " + obj[i]);
}
// 配列の要素が列挙される
for (var i of array1) {
console.log(i);
}
// イテレート可能ではないオブジェクトではfor-of文はエラーとなる
for (var i of obj) { // エラー!
console.log(i);
}
Babelの「Try it out」ページで試してみると分かるが、最後のfor-of文はエラーとなる。
for-in文はオブジェクトが持つ列挙可能なプロパティ(の名前)を取り出す。このため、例えば配列にこれを使おうとすると、配列の要素以外のプロパティまで取り出してしまうなど、使い勝手はよくなかった(もちろん、そのために配列にはforEachメソッドがあるわけだが)。配列以外の要素がないにしても、配列に対してfor-inで取り出せるのはプロパティ(=インデックス)なので、実際に要素を扱うには個々の要素をインデックス指定することになるので少々手間がかかる。
これに対して、for-of文はそのプロパティの値を(配列であれば要素を直接)反復処理できる。ジェネレータ関数と組み合わせれば、コレクションの反復処理がきれいに行えるのは上で見た通りだ(興味のある方はfor-in文とジェネレータ関数を組み合わせて実行してみよう)。
最後に文字列の例を見てみよう。文字列もSymbol.iteratorプロパティを持つイテレート可能なオブジェクトだ。
var str = new String("string");
str[Symbol.iterator] = function() {
var s = this;
var l = s.length;
return {
next: function() {
if (l > 0) {
l--
return { value: s[l], done: false }
} else {
return { done: true }
}
}
}
}
var s = "";
for (var item of str) {
s += item;
}
console.log(s)
Symbol.iteratorプロパティを定義する場合、String型のオブジェクトとして文字列を生成しておく必要がある(基本データ型としての文字列にプロパティを設定しても、アクセス終了後に回収されてしまうため)。
ここでは、String型のオブジェクトを作成し、そのSymbol.iteratorプロパティを設定している。つまり、イテレータオブジェクトを変更している。そのため、この関数はnextメソッドを持つオブジェクトを返すようになっていて、nextメソッドでは文字列の末尾から順に1文字ずつvalueプロパティの値として返送し(このときはdoneプロパティの値はfalse)、最後にdoneプロパティをtrueにしたオブジェクトを返送する。これにより、strオブジェクトを反復処理すると、文字列の個々の要素を逆順に取り出せるようになる(出力結果は「gnirts」となる)。
本稿の最後に、yield式について触れておく。
Copyright© Digital Advantage Corp. All Rights Reserved.