JSにおけるデータ構造のエルゴード--Iteratorとfor...ofサイクル
9938 ワード
JavaScriptは元の4種類の「集合」を表すデータ構造で、Object、Aray、Set、Map.
エルゴード(Iterator)は、様々な異なるデータ構造のための統一的なアクセス機構を提供するインターフェースである.任意のデータ構造は、Iteratorインターフェースを配備すれば、エルゴード動作を完了することができます.
Iteratorの役割は三つあります.一つは各種データ構造のために、統一的で簡便なアクセスインターフェースを提供します.第二は、データ構造のメンバーをある順序で並べられるようにすることである.第三に、ES 6は新しいエルゴードコマンド
Iteratorの遍歴過程はこうです.は、現在のデータ構造の開始位置を指すポインタオブジェクトを作成します.エルゴードオブジェクトは、本質的にはポインタオブジェクトです. ポインタオブジェクトのnextメソッドを初めて呼び出し、ポインタをデータ構造の最初のメンバに向けることができる. ポインタオブジェクトのnextメソッドを第2回呼び出すと、ポインタはデータ構造の第2のメンバを指す. は、ポインタオブジェクトのnextメソッドを、データ構造の終了位置を指すまで継続的に起動する. nextメソッドを呼び出すたびに、データ構造の現在のメンバーの情報を返します.具体的には、valueとdoneの2つの属性を含むオブジェクトを返します.ただし、value属性は現在のメンバの値であり、done属性はブール値であり、遍歴が終了するかどうかを示す.
nextメソッドをシミュレートして値を返した例
Iteratorインターフェースの目的は、すべてのデータ構造のための統一的なアクセス機構、すなわち
ES 6は、デフォルトのIteratorインターフェースがデータ構造のSymbol.iterator属性に配置されていると規定しています.あるいは、一つのデータ構造はSymbol.iterator属性を持つ限り、「巡回可能」と考えられます.
Symbol.iterator属性自体は関数であり、現在のデータ構造のデフォルトのエルゴード生成関数である.この関数を実行すると、エルゴードが返されます.属性名Symbol.iteratorについては、Symbolオブジェクトのiterator属性を返します.これはあらかじめ定義されていて、タイプはSymbolの特殊な値ですので、括弧内に入れます.
ES 6のいくつかのデータ構造は、元々は、配列のようなIteratorインターフェースを備えています.つまり、何も処理しなくても、for...ofループによって巡回されます.これらのデータ構造はSymbol.iterator属性を展開しています.他のいくつかのデータ構造はありません.Symbol.iterator属性が展開されているデータ構造は、エルゴードインターフェースと呼ばれる.このインターフェースを起動すると、エルゴードオブジェクトが返されます. Aray Map セット String TypedAray 関数のargmentsオブジェクト NodeListオブジェクト これらのデータ構造は、元のインターフェースを備えています.
配列のSymbol.iterator属性.
Iteratorインターフェースを原生で展開するデータ構造については、自分でエルゴードを書かずに関数を生成します.for...ofサイクルは自動的にそれらを巡回します.その他のデータ構造(主に対象)のIteratorインターフェースは、Symbol.iterator属性の上に自分で配置する必要があります.
クラスの配列のオブジェクト(数字のキーパッドとlength属性があります)に対して、Iteratorインターフェースを配置します.簡単な方法はSymbol.iteratorメソッドが直接に配列のIteratorインターフェースを参照します.構成値 拡張演算子 yield* for…of Aray.from() Map()、Set()… for...ofサイクル
一つのデータ構造はSymbol.iterator属性を展開すれば、iteratorインターフェースを持っていると見なされ、for...ofサイクルでそのメンバーを遍歴することができます.つまり、for…ofサイクル内部で呼び出したのは、データ構造のSymbol.iterator方法です.
for...ofサイクルが使用できる範囲は、配列、セット、およびMap構造、いくつかの類似配列のオブジェクト(例えば、argmentsオブジェクト、DOM NodeListオブジェクト)、Generatorオブジェクト、および文字列を含む.
JavaScriptの元のfor…inサイクルは、対象のキー名を取得するだけで、直接キーの値を取得することができません.ES 6はfor...ofサイクルを提供し、キーの値を遍歴することができます.
for...ofループは、エルゴードインターフェースを呼び出します.配列のエルゴードインターフェースは、数値インデックスの属性だけを返します.この点はfor…inサイクルとも違います.
普通のオブジェクトに対しては、for...of構造は直接使用できません.エラーが発生します.Iteratorインターフェースを配置してから使用しなければなりません.しかし、このような場合、for…inサイクルは依然としてキーの名前を遍歴するために使用されます.
配列はforループとforEachを使用できます.
しかし、forEachは途中でforEachサイクルを飛び出すことができません.break、returnは効果がありません.
for…inサイクルにはいくつかの欠点があります.
配列のキー名は数字ですが、for...inループは文字列をキー名の「0」、「1」、「2」などとします.
for...inサイクルは、数字キーだけでなく、手動で追加された他のキーを遍歴するだけでなく、プロトタイプチェーン上のキーも含みます.
場合によっては、for…inサイクルは任意の順序でキー名を巡回します.つまり、for…inサイクルは主にオブジェクトを遍歴するために設計されています.遍歴配列には適用されません.
for...of:はfor...inと同じ簡潔な文法を持っていますが、for...inの欠点はありません. はforEach方法と違って、break、continue、returnと一緒に使用できます. は、すべてのデータ構造を巡回する統一動作インターフェースを提供する.
エルゴード(Iterator)は、様々な異なるデータ構造のための統一的なアクセス機構を提供するインターフェースである.任意のデータ構造は、Iteratorインターフェースを配備すれば、エルゴード動作を完了することができます.
Iteratorの役割は三つあります.一つは各種データ構造のために、統一的で簡便なアクセスインターフェースを提供します.第二は、データ構造のメンバーをある順序で並べられるようにすることである.第三に、ES 6は新しいエルゴードコマンド
for...of
ループを作成しました.Iteratorインターフェースは主にfor...of消費に提供されます.Iteratorの遍歴過程はこうです.
nextメソッドをシミュレートして値を返した例
let it = makeIterator(['a','b']);
it.next();// { value: "a", done: false }
it.next();// { value: "b", done: false }
it.next();// { value: undefined, done: true }
fucntion makeIterator(array){
let nextIndex = 0;
return {
next : function(){
return nextInedx < array.length ?
{value : array[nextIndex++], done : false} :
{value : undefined, done : true};
}
}
}
デフォルトIteratorインターフェースIteratorインターフェースの目的は、すべてのデータ構造のための統一的なアクセス機構、すなわち
for...of
サイクルを提供することである.for...of
を使用してあるデータ構造を巡回すると、このサイクルは自動的にIteratorインターフェースを探しに行く.ES 6は、デフォルトのIteratorインターフェースがデータ構造のSymbol.iterator属性に配置されていると規定しています.あるいは、一つのデータ構造はSymbol.iterator属性を持つ限り、「巡回可能」と考えられます.
Symbol.iterator属性自体は関数であり、現在のデータ構造のデフォルトのエルゴード生成関数である.この関数を実行すると、エルゴードが返されます.属性名Symbol.iteratorについては、Symbolオブジェクトのiterator属性を返します.これはあらかじめ定義されていて、タイプはSymbolの特殊な値ですので、括弧内に入れます.
const obj = {
[Symbol.iterator] : function(){
return {
next : function(){
return {
value : 1,
done : true
}
}
}
}
};
上のコードでは、オブジェクトobjはSymbol.iterator属性を持つので、巡回可能です.この属性を実行すると、エルゴードオブジェクトが返されます.このオブジェクトの基本的な特徴は、nextメソッドを持つことです.nextメソッドを呼び出すたびに、現在のメンバーを表す情報オブジェクトが返されます.valueとdoneの2つの属性があります.ES 6のいくつかのデータ構造は、元々は、配列のようなIteratorインターフェースを備えています.つまり、何も処理しなくても、for...ofループによって巡回されます.これらのデータ構造はSymbol.iterator属性を展開しています.他のいくつかのデータ構造はありません.Symbol.iterator属性が展開されているデータ構造は、エルゴードインターフェースと呼ばれる.このインターフェースを起動すると、エルゴードオブジェクトが返されます.
配列のSymbol.iterator属性.
let arr = ['a','b','c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
上のコードでは、変数arrは配列であり、元はエルゴードインターフェースを持ち、arrのSymbol.iterator属性の上に配置されています.したがって、この属性を呼び出すと、エルゴードオブジェクトが得られます.Iteratorインターフェースを原生で展開するデータ構造については、自分でエルゴードを書かずに関数を生成します.for...ofサイクルは自動的にそれらを巡回します.その他のデータ構造(主に対象)のIteratorインターフェースは、Symbol.iterator属性の上に自分で配置する必要があります.
for...of
によってIteratorインターフェースを循環的に起動するには、Symbol.iteratorの属性に属性エルゴード生成方法を配置しなければならない.class RangeIterator {
constructor(start, stop){
this.value = start;
this.stop = stop;
}
[Symbol.iterator]{return this;}
next(){
let value = this.value;
if(value < this.stop){
this.value++;
return {done : false, value : value};
}
return {done : true, value : undefined};
}
}
function range(start, stop){
return new RangeIterator(start, stop);
}
for(let v of range(0,3)) {
console.log(value); // 0 1 2
}
以下は、エルゴードによりポインタ構造を実現する例である.function Obj(value){
this.value = value;
this.next = null;
}
Obj.prototype[Symbol.iterator] = function(){
let iterator = {next : next}; //
let current = this;
// next
function next(){
console.log(current);
if(current){
let value = current.value;
current = current.next;
return {done : false, value : value} //return
}else{
return {done : true};
}
}
return iterator;
}
let one = new Obj(1);
let two = new Obj(2);
let three = new Obj(3);
one.next = two;
two.next = three;
for(let i of one){
console.log(i);
}
//Obj {value: 1, next: Obj}
//1
//Obj {value: 2, next: Obj}
//2
//Obj {value: 1, next: null}
//3
上記のコードはまず構造関数のプロトタイプチェーンにSymbol.iterator方法を展開し、この方法を呼び出してエルゴードオブジェクトiteratorに戻り、オブジェクトのnextメソッドを呼び出し、値を返しながら、内部ポインタ(上のcreate)を次の例に向ける.注意:毎回、巡回対象のnextを呼び出す方法です.クラスの配列のオブジェクト(数字のキーパッドとlength属性があります)に対して、Iteratorインターフェースを配置します.簡単な方法はSymbol.iteratorメソッドが直接に配列のIteratorインターフェースを参照します.
let likeArray = {
0 : 'a',
1 : 'b',
2 : 'c',
length : 3,
[Symbol.iterator] : Array.prototype[Symbol.iterator]// [][Symbol.iterator]
};
for(let item of iterable){
console.log(item) //'a','b','c'
}
一般的なオブジェクト配列のSymbol.iteratorメソッドは効果がありません.Object.prototype[Symbol.iterator] = () => {
let index = 0;
console.log(this === window); //true
let keys = Object.keys(this);
let length = keys.length;
let self = this;
return {next : next};
function next(){
if(index++ < length){
return {
value : self[keys[index]],
done : false,
};
}else{
return {
done : true
};
}
}
}
Iteratorインターフェースを呼び出す場合let set = new Set().add('a').add('b').add('c');
let [x,y] = set;
//x ='a' y = 'b'
let [first, ...rest] = set;
//first = 'a'; rest = ['b','c'];
//
var str = 'hello';
[...str] // ['h','e','l','l','o']
//
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']
yield*
の後に付いているのは、巡回可能な構造であり、構造のエルゴードインターフェースを起動する.let generator = function* () {
yield 1;
yield* [2,3,4];
yield 5;
};
var iterator = generator();
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }
一つのデータ構造はSymbol.iterator属性を展開すれば、iteratorインターフェースを持っていると見なされ、for...ofサイクルでそのメンバーを遍歴することができます.つまり、for…ofサイクル内部で呼び出したのは、データ構造のSymbol.iterator方法です.
for...ofサイクルが使用できる範囲は、配列、セット、およびMap構造、いくつかの類似配列のオブジェクト(例えば、argmentsオブジェクト、DOM NodeListオブジェクト)、Generatorオブジェクト、および文字列を含む.
JavaScriptの元のfor…inサイクルは、対象のキー名を取得するだけで、直接キーの値を取得することができません.ES 6はfor...ofサイクルを提供し、キーの値を遍歴することができます.
var arr = ['a', 'b', 'c', 'd'];
for (let a in arr) {
console.log(a); // 0 1 2 3
}
for (let a of arr) {
console.log(a); // a b c d
}
for...ofループを通して配列のインデックスを取得する場合は、配列のインスタンスのentriesメソッドとkeysメソッドを利用しても良いです.for...ofループは、エルゴードインターフェースを呼び出します.配列のエルゴードインターフェースは、数値インデックスの属性だけを返します.この点はfor…inサイクルとも違います.
let arr = [3, 5, 7];
arr.foo = 'hello';
for (let i in arr) {
console.log(i); // "0", "1", "2", "foo"
}
for (let i of arr) {
console.log(i); // "3", "5", "7"
}
let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
console.log(pair);
}
// ['a', 1]
// ['b', 2]
for (let [key, value] of map) {
console.log(key + ' : ' + value);
}
// a : 1
// b : 2
let arrayLike = { length: 2, 0: 'a', 1: 'b' };
//
for (let x of arrayLike) {
console.log(x);
}
//
for (let x of Array.from(arrayLike)) {
console.log(x);
}
普通のオブジェクトに対して普通のオブジェクトに対しては、for...of構造は直接使用できません.エラーが発生します.Iteratorインターフェースを配置してから使用しなければなりません.しかし、このような場合、for…inサイクルは依然としてキーの名前を遍歴するために使用されます.
let es6 = {
edition: 6,
committee: "TC39",
standard: "ECMA-262"
};
for (let e in es6) {
console.log(e);
}
// edition
// committee
// standard
for (let e of es6) {
console.log(e);
}
// TypeError: es6[Symbol.iterator] is not a function
一つの解決方法は、オブジェクトのキー名をObject.keys法を用いて配列を生成し、この配列を巡回することである.for (var key of Object.keys(someObject)) {
console.log(key + ': ' + someObject[key]);
}
もう一つの方法はGenerator関数を使って物体を再包装することです.function* entries(obj) {
for (let key of Object.keys(obj)) {
yield [key, obj[key]];
}
}
for (let [key, value] of entries(obj)) {
console.log(key, '->', value);
}
// a -> 1
// b -> 2
// c -> 3
他のエルゴード文法との比較配列はforループとforEachを使用できます.
しかし、forEachは途中でforEachサイクルを飛び出すことができません.break、returnは効果がありません.
for...in
は、配列のキー名を巡回してもよい.ただし、キーが数字の文字列は「0」を表します.for…inサイクルにはいくつかの欠点があります.
配列のキー名は数字ですが、for...inループは文字列をキー名の「0」、「1」、「2」などとします.
for...inサイクルは、数字キーだけでなく、手動で追加された他のキーを遍歴するだけでなく、プロトタイプチェーン上のキーも含みます.
場合によっては、for…inサイクルは任意の順序でキー名を巡回します.つまり、for…inサイクルは主にオブジェクトを遍歴するために設計されています.遍歴配列には適用されません.
for...of:
for (var n of fibonacci) {
if (n > 1000)
break;
console.log(n);
}