Iteratorについて
前回は「Symbol」について説明しました
今回は「Iterator」についてです
「Iterator」、「Iterable」の順番で説明します
Iterator
Iteratorとは・・
- 次の要素(IteratorResult)へ1つずつアクセスする方法を備えたオブジェクト
- nextメソッドを持っていて、nextメソッドがIteratorResultを返す
- IteratorResultはvalueプロパティとdoneプロパティを持っているオブジェクト
文章だとよくわかりませんね汗
上記の定義をコードにするとこんな感じになります
var index = 0; var array = [1, 2, 3]; var iterator = {}; // Iteratorはオブジェクト iterator.next = function () { // nextメソッドを持っている // valueプロパティとdoneプロパティを持っているIteratorResult var iteratorResult = { value: array[index++], done: index > array.length }; return iteratorResult; // nextメソッドはIteratorResultを返す };
コードにするとたったこれだけです
Iteratorは次の要素へのアクセスする方法を提供するだけで 実際の繰り返し処理は外部に任せます
IteratorResultの各プロパティですが
Iterable
Iterableとは・・
これも文章だとさっぱりですねw
コードで表現してみます
var iterable = {}; // Iterableはオブジェクト iterable[Symbol.iterator] = function() { // [Symbol.iterator]メソッドを持ってる return iterator; // Iteratorを返す };
前回説明したSymbol.iteratorを使ってますね
それ以外は簡単なコードです
では実際にIteratorを使ってみます
実践
var obj = {}; obj[Symbol.iterator] = function() { var index = 0; var array = [1, 2, 3]; var iterator = {}; iterator.next = function () { var iteratorResult = { value: array[index++], done: index > array.length }; return iteratorResult; }; return iterator; }; var iterator = obj[Symbol.iterator](); console.log(iterator.next()); // Object {value: 1, done: false} console.log(iterator.next()); // Object {value: 2, done: false} console.log(iterator.next()); // Object {value: 3, done: false} console.log(iterator.next()); // Object {value: undefined, done: true} /* whileを使った場合 var iterator = obj[Symbol.iterator](); var iteratorResult; while (true) { iteratorResult = iterator.next(); if (iteratorResult.done) { break; } console.log(iteratorResult.value); } */
上記を実際に実行してみると、順番に値を取り出すことができます
ただ、この書き方だと値の取り出し方が冗長でめんどくさいですね
そこでES2015で用意されたのがfor (var v of iterable)
構文です
上記の繰り返し処理はこんな感じで書けます
for (let v of obj) { console.log(v); }
スッキリしましたね
Iterableの定義さえ守っていれば、自作のオブジェクトもfor (var v of iterable)
構文に渡せます
内部でwhileを使った場合の処理が行われています
ビルトインのString、Array、TypedArray、Map、Setなども反復処理が可能なオブジェクトです
for (let v of [0, 1, 2]) console.log(v); /* 0 1 2 */ for (let v of 'abc') console.log(v); /* a b c */
まとめ
- Iteratorはnextメソッドを持ち、nextメソッドを呼ぶとIteratorResultを返す
- Iterableは[Symbol.iterator]メソッドを持ち、[Symbol.iterator]メソッドを呼ぶとIteratorを返す
for (var v of iterable)
構文を使うと、簡単に値を取り出せる
そして、ジェネレータ関数で作成されるジェネレータがIterableなオブジェクトです
詳しくは次の回で
Symbolについて
ES2015(ES6)で追加されたgenerators(yield)
generatorsを使うにはSymbol、Iteratorを理解しておいた方がいいので順番に説明していきます
今回は「Symbol」についてです
概要
ES2015で追加された新しいプリミティブです
数値でも文字列でも真偽値でもない値ですが
文字列のようにオブジェクトのプロパティのキーとして使えます
またObject.keys、Object.getOwnPropertyNamesでも列挙できないという特徴を持っています
作り方
var s = Symbol(); var s = Symbol('foo'); // 引数に文字列を渡すことでSymbolの説明を追加することができる
念のためtypeof演算子で確認するとSymbolであることがわかります
typeof s; // "symbol"
またnew演算子付きで呼ぶとType Errorとなるので注意
var s = new Symbol(); // Type Error
使い方
Symbolはオブジェクトのプロパティのキーにすることができます
var obj = {}; var s = Symbol(); obj[s] = 'hoge'; console.log(obj[s]); // hoge
ちなみに、ES2015のComputed Property Namesを使えば下記のように書けます
var s = Symbol(); var obj = { [s]: 'hoge' }; console.log(obj[s]); // hoge
シンボルは暗黙的に文字列変換されるわけではないので、下記の場合undefinedになります
console.log(obj[s.toString()]); // undefined
特徴
Symbolは毎回異なるシンボルが作成されます
Symbol('foo') === Symbol('foo'); // false
この特徴を活かして、ES2015は既存のコードに影響がでないように
機能を追加できるようにしたようです
例えば、
function test(obj, value) { obj._value = value; }
上記のようなコードの場合、外部から勝手にobjのプロパティを上書きされて全く別のものになったり
キーがシンボルではないので普通に外部から参照できてしまいます
↓このようにすることで、外部からいじられる心配も、参照される心配もありません
var obj = {}; var _value = Symbol(); function test(obj, value) { obj[_value] = value; } test(obj, 123);
キーがシンボルなので同じキーを外部から作れないためです
ただし、Object.getOwnPropertySymbolsでシンボルを取得できてしまうので
完全に隠蔽できるわけではないです
console.log(obj[Object.getOwnPropertySymbols(obj)[0]]); // 123
登録
シンボルは毎回異なるシンボルが作成されますが
Symbol.forを使ってシンボルを登録すると共有することが可能です
引数で与えられたキーでランタイム全体のシンボルレジストリ内に存在しているシンボルを検索し、見つかった場合それを返します。さもなければ、新しいシンボルがこのキーでグローバルシンボルレジストリ内に生成されます。
↓こんな感じでシンボルを共有できます
var s1 = Symbol.for('foo'); // ここでは新しく作成される var s2 = Symbol.for('foo'); // 作成されているシンボルを参照 var s3 = Symbol('foo'); // これはグローバルシンボルレジストリに生成されてないシンボルの作成 s1 === s2; // true s1 === s3; // false
Well-Known Symbols
ウェルノウンシンボルという特別なシンボルがあります
「特別なシンボル」と書くと、何かすごい機能があるように思えますが、ごく普通のシンボルです
JavaScriptの内部処理で参照されるシンボルです
このウェルノウンシンボルの中にSymbol.iteratorというシンボルがあります
IteratorはこのSymbol.iteratorを使うことで実現します
ブログ開設します
ブログの名前「function」はJavaScriptのfunctionからとってます
JavaScriptのfunctionが
- 変数に代入できる
- 関数に渡せる
- 関数の戻り値にできる
- 実行時に関数の中身を変えられる
こんな感じでいろいろできることに習って
自分も何でもできるエンジニアになりたいという願望から名付けました
ブログの内容的にはWeb技術全般について書いていこうかなって思ってます