JavaScriptのユーザクラスをfor of構文でループ可能にする方法。Symbol.iteratorでiteratorプロトコルを実装したオブジェクトを返す関数をセットするか、ジェネレータ関数を実装すれば良い。楽なのは後者
class Hoge { *[Symbol.iterator]() { yield 1; yield 2; yield 3; } } const o = new Hoge(); for (const i of o) { console.log(i) // => 1, 2, 3 }
Symbol.iteratorを使ってRangeクラスを作成。挙動はpythonのrange関数を参考にした。
class Range { constructor(begin = 0, end = 0, step = 1) { if (arguments.length === 0) { throw new Error('expected at least 1 arguments, got 0') } else if (arguments.length === 1) { end = begin; begin = 0; } this._begin = begin; this._end = end; this._step = step; if (step === 0) throw new Error('step argument must not be 0') } *[Symbol.iterator]() { if (this._step > 0) { for (let i = this._begin; i < this._end; i += this._step) yield i; } else { for (let i = this._begin; i > this._end; i += this._step) yield i; } } get length() { const len = Math.ceil((this._end - this._begin) / this._step); return len > 0 ? len : 0; } }
const r = new Range(1, 9, 2); for (const i of r) { console.log(i); // => 1, 3, 5, 7 } // 配列に変換 console.log(Array.from(r)); // [1 ,3, 5, 7] // 分割代入 const [head, ...tail] = r; console.log(head); // 1 console.log(tail); // [3, 5, 7] // 分割代入で配列化 console.log(Array.of(...r)); console.log([...r]);
正直rangeはネイティブオブジェクトとして提供していいと思うのだが…