ES6のIterator(イテレータ)

コンセプト

Ruan Yifeng 先生の ES6 を参照してください。

JavaScript 本来の「コレクション」を表すデータ構造は主に配列 ( Array ) とオブジェクト ( Object ) ですが、ES6 ではMapSetが追加されました。このようにデータセットは 4 種類あり、配列のメンバーは Map、Map のメンバーはオブジェクトなど、ユーザーがこれらを組み合わせて独自のデータ構造を定義することもできます。これには、 さまざまなデータ構造をすべて処理するための統一されたインターフェイス メカニズムが必要です

イテレータとはそのような仕組みです。これは、さまざまなデータ構造に対する統合アクセス メカニズムを提供するインターフェイスですデータ構造がIteratorインターフェイスをデプロイしている限りトラバーサル操作を完了できます(つまり、データ構造のすべてのメンバーが順番に処理されます)。

イテレータには 3 つの関数があります。

  1. 1 つは、さまざまなデータ構造に統一された便利なアクセス インターフェイスを提供することです。
  2. 2 つ目は、データ構造のメンバーを特定の順序で配置できるようにすることです。
  3. 3 つ目は、ES6 が新しいトラバーサル コマンドfor...ofサイク​​ルを作成し、Iterator インターフェイスが主に for...of 消費に使用されることです

横断用

Iterator インターフェイスの目的は、すべてのデータ構造に対する統合アクセス メカニズム、つまり for...of ループを提供することです (詳細については以下を参照)。for...of ループを使用して特定のデータ構造を走査すると、ループは自動的に Iterator インターフェイスを探します。

データ構造が Iterator インターフェースを展開する限り、このデータ構造を「トラバース可能」(反復可能) と呼びます。

Iterator インターフェイスを備えたネイティブ データ構造は次のとおりです。

  • 配列
  • 地図
  • 設定
  • 型付き配列
  • 関数の引数オブ​​ジェクト
  • NodeList オブジェクト

オブジェクトにイテレータを追加

簡単な例

let obj = {
  data:  ['a', 'b', 'c'],
  [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index < self.data.length) {
          return {
            value: self.data[index++],
            done: false
          };
        }
        return { value: undefined, done: true };
      }
    };
  }
};
for (let item of obj) {
  console.log(item); // 'a', 'b', 'c'
}

収量を使用する

Generator 関数を使用した、Symbol.iterator()メソッド の最も単純な実装。

let myIterable = {
  [Symbol.iterator]: function* () {
    yield 1;
    yield 2;
    yield 3;
  }
};
[...myIterable] // [1, 2, 3]

// 或者采用下面的简洁写法

let obj = {
  * [Symbol.iterator]() {
    yield 'hello';
    yield 'world';
  }
};

for (let x of obj) {
  console.log(x);
}
// "hello"
// "world"

Symbol.iterator()メソッドではコードをデプロイする必要はほとんどなく、yieldコマンドを使用して各ステップの戻り値を与えるだけです。 


同様の配列オブジェクトは Symbol.iterator を呼び出します

配列に似たオブジェクト。前のキーは配列のインデックス(0 1 2...) に似ているため、for of で走査できます。

let iterable = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); // 'a', 'b', 'c'
}

通常のオブジェクト展開配列のSymbol.iteratorメソッドは効果がないことに注意してください。

let iterable = {
  a: 'a',
  b: 'b',
  c: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); // undefined, undefined, undefined
}

Iteratorインターフェースを呼び出す場合

場合によっては、Iterator インターフェイス (つまり、Symbol.iterator メソッド) がデフォルトで呼び出されます。for...of ループに加えて、他にもいくつかの場合があります。

代入の構造化

Symbol.iteratorメソッドは、配列およびSet構造体を構造化して代入するときに、デフォルトで呼び出されます

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'];

スプレッド演算子

スプレッド演算子 (...)も、デフォルトの Iterator インターフェイスを呼び出します。

// 例一
var str = 'hello';
[...str]  //  ['h','e','l','l','o']

// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']

上記のコードの拡張演算子は、内部でIteratorインターフェイスを呼び出します。

実際、これは、Iterator インターフェイスを実装するデータ構造を配列に変換する便利なメカニズムを提供します。

つまり、データ構造が Iterator インターフェイスを実装している限り、スプレッド演算子を使用して配列に変換できます

let arr = [...iterable];

収率*

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 }

その他の機会

配列のトラバーサルはトラバーサー インターフェイスを呼び出すため、配列をパラメーターとして受け入れる場合は実際にトラバーサー インターフェイスを呼び出します。

ここではいくつかの例を示します

  • Array.from()
  • Map()、Set()、WeakMap()、WeakSet() (例: new Map([['a',1],['b',2]]))
  • Promise.all()
  • Promise.race()

配列トラバーサルのいくつかの方法

for 循環 forEach for in for of

それぞれに

forEach ループから抜け出す方法はなく、break コマンドも return コマンドも機能しません。
 


のために

for...in ループは、配列のキーを反復処理できます。

for...in ループにはいくつかの欠点があります。

  • 配列のキーは数値ですが、for...in ループには文字列「0」、「1」、「2」などがキーとして設定されます。
  • for...in ループは、数値キー名だけでなく、手動で追加された他のキー、さらにはプロトタイプ チェーン上のキーも反復します
  • 場合によっては、for...in ループが任意の順序でキーを反復処理します。

結論として、 for...in ループは主に、配列を走査するためではなく、オブジェクトを走査するために設計されています

let arr = [3, 5, 7];
arr.foo = 'hello';
for (let i in arr) {
  console.log(i); // "0", "1", "2", "foo"
}

  • for...in と同じ簡潔な構文を持ちますが、for...in の欠点はありません。
  • forEachメソッドとは異なり、 Breakcontinuereturnと組み合わせて使用​​できます
  • すべてのデータ構造を横断するための統合された操作インターフェイスを提供します。

以下は、break ステートメントを使用して for...of ループから抜け出す例です。

for (var n of fibonacci) {
  if (n > 1000)
    break;
  console.log(n);
}

上記の例では、フィボナッチ数列が 1000 以下の項目を出力します。現在の項目が 1000 より大きい場合

これは、breakステートメントを使用してfor...ofループから抜け出します


注意(オブジェクト)

このオブジェクトはIteratorインターフェイスをデプロイしませんが、代入拡張演算子の構造化などの 操作 を実行できます。

代入の構造化

let obj = {
  a: 1,
  b: 2,
  c: 3
};

const {a, b} = obj;
console.log(a, b)  // 1 2

... スプレッド演算子

let obj = {
  a: 1,
  b: 2,
  c: 3
};

const a = {...obj};
console.log(a)  // {a: 1, b: 2, c: 3}

理由

オブジェクトの分割はIteratorイテレータとは何の関係もありません

オブジェクトの分解は、新しいオブジェクトの作成 -> 属性の列挙 -> 属性のコピーと値の割り当てです。


記事に間違いがありましたら、ぜひ皆さんから質問していただければ幸いです。分からない場合はコメントしていただければ一つ一つ返信させていただきます。

もしこの記事が皆様のお役に立てましたら、高評価をして励ましていただければ幸いです、今後一緒に仕事をするまでの道のりは長く険しいと思います。

おすすめ

転載: blog.csdn.net/qq_52855464/article/details/130793770