[ES6] Ruan Yifeng ES6 学習イテレータと for...of ループ

1. イテレータ

1.コンセプト

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

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

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

2.動作原理

  1. 現在のデータ構造の先頭を指すポインター オブジェクトを作成します。言い換えれば、トラバーサー オブジェクトは本質的にはポインター オブジェクトです。

  2. nextポインター オブジェクトのメソッドが初めて呼び出されるとき、ポインターはデータ構造の最初のメンバーを指すことができます。

  3. ポインター オブジェクトのメソッドが 2 回目に呼び出されるときnext、ポインターはデータ構造の 2 番目のメンバーを指します。

  4. nextデータ構造の終わりを指すまで、ポインター オブジェクトのメソッドを呼び出し続けます。

メソッドが呼び出されるたびに、2 つのプロパティを含むオブジェクトnextが返されます。このうち、属性は現在のメンバーの値であり、属性は走査が終了したかどうかを示すブール値です。valuedonevaluedone

var it = makeIterator(['a', 'b']);

it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }

function makeIterator(array) {
    
    
  var nextIndex = 0;
  return {
    
    
    next: function() {
    
    
      return nextIndex < array.length ?
        {
    
    value: array[nextIndex++], done: false} :
        {
    
    value: undefined, done: true};
    }
  };
}

3. デフォルトの Iterator インターフェース

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

ES6 では、デフォルトの Iterator インターフェイスがSymbol.iteratorデータ構造のプロパティにデプロイされること、つまり、データ構造にプロパティがある限り、Symbol.iteratorそれは「反復可能」であると見なすことができると規定しています。

Symbol.iterator属性自体は関数であり、現在のデータ構造のデフォルトのトラバーサー生成関数です。この関数を実行するとイテレータが返されます。

const obj = {
    
    
  [Symbol.iterator] : function () {
    
    
    return {
    
    
      next: function () {
    
    
        return {
    
    
          value: 1,
          done: true
        };
      }
    };
  }
};

ES6 は新しいトラバーサル コマンドfor…ofループを作成します。Iterator インターフェイスは主にfor…of使用されます。

イテレータインターフェイスを備えたネイティブデータ (トラバース可能for...of)

  • 配列
  • 地図
  • 設定
  • 型付き配列
  • 関数の引数オブ​​ジェクト
  • NodeList オブジェクト
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 }

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

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

(1) 代入の分割

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

(2) スプレッド演算子

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

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

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

(3) 収量*

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 } 

(4) その他の機会

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

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

2. for...of ループ

1. アレイ

const arr = ['red', 'green', 'blue'];

for(let v of arr) {
    
    
  console.log(v); // red green blue
}

for...ofループは、配列インスタンスのメソッドの代わりに使用できますforEach

const arr = ['red', 'green', 'blue'];

arr.forEach(function (element, index) {
    
    
  console.log(element); // red green blue
  console.log(index);   // 0 1 2
});

JavaScript本来のfor...inループはオブジェクトのキー名を取得することしかできず、キー値を直接取得することはできません。ES6 は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ループは反復子インターフェイスを呼び出し、配列の反復子インターフェイスは数値インデックスを持つプロパティのみを返します。これは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"
}

上記のコードでは、for...ofループは配列 arr の foo プロパティを返しません。

2. 構造の設定とマップ

var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
    
    
  console.log(e);
}
// Gecko
// Trident
// Webkit

var es6 = new Map();
es6.set("edition", 6);
es6.set("committee", "TC39");
es6.set("standard", "ECMA-262");
for (var [name, value] of es6) {
    
    
  console.log(name + ": " + value);
}
// edition: 6
// committee: TC39
// standard: ECMA-262

3. 配列状のオブジェクト

配列のようなオブジェクトには、いくつかのクラスが含まれます。以下は、文字列、DOM NodeList オブジェクト、および引数オブジェクトの for...of ループの例です。

// 字符串
let str = "hello";

for (let s of str) {
    
    
  console.log(s); // h e l l o
}

// DOM NodeList对象
let paras = document.querySelectorAll("p");

for (let p of paras) {
    
    
  p.classList.add("test");
}

// arguments对象
function printArgs() {
    
    
  for (let x of arguments) {
    
    
    console.log(x);
  }
}
printArgs('a', 'b');
// 'a'
// 'b'

すべての配列のようなオブジェクトに Iterator インターフェイスがあるわけではありません。簡単な解決策は、Array.from メソッドを使用して配列に変換することです。

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);
}

4. オブジェクト

通常のオブジェクトの場合、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

上記のコードは、通常のオブジェクトの場合、for...inループがキー名を走査でき、for...ofループがエラーを報告することを示しています。

1 つの解決策は、メソッドを使用してObject.keysオブジェクトのキー名の配列を生成し、その配列を走査することです。

for (var key of Object.keys(someObject)) {
    
    
  console.log(key + ': ' + someObject[key]);
}

もう 1 つの方法は、Generator 関数を使用してオブジェクトを再パックすることです。

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

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

3. 他のトラバーサル構文との比較

配列を例に挙げると、JavaScript はさまざまなトラバーサル構文を提供します。最も原始的な記述方法は for ループです。

for (var index = 0; index < myArray.length; index++) {
    
    
  console.log(myArray[index]);
}

この書き方は面倒なので、配列には組み込みforEachメソッドが用意されています。

myArray.forEach(function (value) {
    
    
  console.log(value);
});

forEachこの書き方の問題は、ループから抜け出す方法がなく、breakコマンドもreturnコマンドも機能しないことです。

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

for (var index in myArray) {
    
    
  console.log(myArray[index]);
}

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

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

つまり、for...inこのサイクルは主に次のようなものです。オブジェクトを横断する設計上、配列の走査には適していません。

for...of上記のいくつかのアプローチと比較して、ループにはいくつかの重要な利点があります。

for (let value of myArray) {
    
    
  console.log(value);
}
  1. 同じ簡潔な構文を持ちますfor...inが、for...inそれらの欠点はありません。
  2. メソッドとは異なりforEachbreak、continue和returnで使用できます。
  3. すべてのデータ構造を横断するための統合された操作インターフェイスを提供します。

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

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

上記の例では、フィボナッチ数列が 1000 以下の項目を出力します。現在の項目が 1000 より大きい場合、ループをbreak抜け出すためにステートメントが使用されます。for...of

おすすめ

転載: blog.csdn.net/Bon_nenul/article/details/128236461