ホームページ  >  記事  >  ウェブフロントエンド  >  ES6イテレータとfor.ofループの使い方

ES6イテレータとfor.ofループの使い方

小云云
小云云オリジナル
2018-02-09 10:05:181165ブラウズ

この記事では主にES6のイテレータ(Iterator)とfor.ofループの使い方の学習まとめを紹介していますが、編集者がとても良いと思ったので、参考としてシェアさせていただきます。編集者をフォローして見てみましょう。皆さんのお役に立てれば幸いです。

1. イテレーターとは何ですか?

ジェネレーターの概念は Java、Python、その他の言語で利用でき、ES6 は JavaScript にも追加されました。 Iterator を使用すると、コレクション変数とインデックス変数を初期化する必要がなくなり、代わりに反復子オブジェクトの next メソッドを使用してコレクション内の次の項目の値を返します。これはプログラミングに特化しています。

イテレータは特別なインターフェイスを持つオブジェクトです。 next() メソッドが含まれており、この呼び出しは 2 つの属性、つまり value と Done を含むオブジェクトを返します。Value は現在の位置の値を表し、done は反復が完了したかどうかを表します。これが true の場合、next の呼び出しは無効になります。

ES5 のコレクションの走査は通常 for ループであり、配列にも forEach メソッドがあり、ES6 では Map と Set が追加され、イテレーターはすべてのコレクション データを統一的に処理できます。イテレータはインターフェイスです。データ構造がイテレータ インターフェイスを公開している限り、反復を完了できます。 ES6 では新しいトラバーサル コマンド for...of ループが作成され、Iterator インターフェイスは主に for...of による消費に使用されます。

2. イテレータの使い方

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

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

    array
  1. map
  2. string
  3. typedArray(一般的な固定長バッファータイプ、バッファ内のバイナリデータを読み取る)
  4. 関数内の
  5. NodeListオブジェクト
  6. argumentsオブジェクト 上記のネイティブデータ構造にはオブジェクト(Object)が存在しないことがわかります。
  7. それは、オブジェクト属性の走査順序が不確実であり、開発者が手動で指定する必要があるためです。本質的に、トラバーサーは線形プロセスです。非線形データ構造の場合、トラバーサー インターフェイスの展開は線形変換の展開と同じです。

次の処理を実行して、オブジェクトを消費できるようにします:

// code1
function Obj(value) {
  this.value = value;
  this.next = null;
}
Obj.prototype[Symbol.iterator] = function() {
  var iterator = {
    next: next
  };
  var current = this;
  function next() {
    if (current) {
      var value = current.value;
      current = current.next;
      return {
        done: false,
        value: value
      };
    } else {
      return {
        done: true
      };
    }
  }
  return iterator;
}
var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3);
one.next = two;
two.next = three;
for (var i of one) {
  console.log(i);
}
// 1
// 2
// 3
2. Iterator インターフェースが呼び出されたとき


(1) 代入の構造化

// code2
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) 拡張演算子

// code3
// 例一
var str = 'hello';
[...str] // ['h','e','l','l','o']
// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']

( 3) Generator 関数の yield* 式 (次の章で紹介します)

// code4
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) その他の場合

for..of

    Array.from
  1. Map(), Set( )、WeakMap ()、WeakSet()
  2. Promise.all()
  3. Promise.race()
  4. 3. for...of ループの利点
  5. まず欠点を見てみましょう。 forEach メソッドの配列:

    // code5
    myArray.forEach(function (value) {
     console.log(value);
    });
  6. この書き方の問題は、forEach ループを途中で抜け出すことができず、break コマンドも return コマンドも有効にならないことです。

オブジェクト for...in ループの欠点をもう一度見てください:

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

配列のキー名は数字ですが、for...in ループではキー名として文字列「0」、「」が使用されます。 1 」、「2」など

    for...in ループは、数値キー名を走査できるだけでなく、手動で追加されたピリオドの推奨事項 (プロトタイプ チェーン上のキーも含む) も走査できます。
  1. 場合によっては、for...in ループセッションはキー名を任意の順序で走査します
  2. for...in 走査は主にオブジェクトの走査用に設計されており、配列の走査には適していません
  3. 、for...of の重要な利点は何ですか?
  4. は、for...inと同じ簡潔な構文を持ちますが、for...inの欠点はありません

    forEachメソッドとは異なり、break、 continue、returnとともに使用できます
  1. すべてのデータ構造を横断するための統合操作インターフェースを提供
  2. for (var n of fibonacci) {
     if (n > 1000) {
      break;
      console.log(n);
     }
    }
  3. 4. データ型ごとに for...of ループを使用するにはどうすればよいですか?
  4. (1) Array

  5. 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 ループはトラバーサー インターフェイスを呼び出し、配列のトラバーサー インターフェイスは数値のみを返しますIndex

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"
}

(2) Map 構造体と Set 構造体

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
上記のコードからわかるように、for...of ループが Map 構造体と Set 構造体を走査するとき、走査の順序は、各メンバーはデータ構造に追加されます。 Set 構造を横断する場合は値が返され、Map 構造を横断する場合は配列が返されます。配列の 2 つのメンバーは、現在のキー名とキー値です。マップメンバー。


(3) 配列状オブジェクト

String

// 普通的字符串遍历
let str = "yuan";
for (let s of str) {
 console.log(s); // y u a n
}

// 遍历含有 32位 utf-16字符的字符串
for (let x of 'a\uD83D\uDC0A') {
 console.log(x);
}
// 'a'
// '\uD83D\uDC0A'
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", "n");
// "a"
// "n"

配列状オブジェクトの走査処理用の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 インターフェイスをデプロイする必要があります。次の 2 つの方法でデプロイします:

// 方法一:使用 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
3. イテレーター アプリケーションの例

1、斐波那契数列

下面我们就使用迭代器来自定义自己的一个斐波那契数列组,我们直到斐波那契数列有两个运行前提,第一个前提是初始化的前两个数字为0,1,第二个前提是将来的每一个值都是前两个值的和。这样我们的目标就是每次都迭代输出一个新的值。

var it = { [Symbol.iterator]() {
    return this
  },
  n1: 0,
  n2: 1,
  next() {
    let temp1 = this.n1,
    temp2 = this.n2;
    [this.n1, this.n2] = [temp2, temp1 + temp2]
    return {
      value: temp1,
      done: false
    }
  }
}

for (var i = 0; i < 20; i++) {
  console.log(it.next())
}

// 
  "value": 0,
  "done": false
} {
  "value": 1,
  "done": false
} {
  "value": 1,
  "done": false
} {
  "value": 2,
  "done": false
} {
  "value": 3,
  "done": false
} {
  "value": 5,
  "done": false
}... {
  "value": 2584,
  "done": false
} {
  "value": 4181,
  "done": false
}

2、任务队列迭代器

我们可以定义一个任务队列,该队列初始化时为空,我们将待处理的任务传递后,传入数据进行处理。这样第一次传递的数据只会被任务1处理,第二次传递的只会被任务2处理… 代码如下:

var Task = {
  actions: [],
  [Symbol.iterator]() {
    var steps = this.actions.slice();
    return { [Symbol.iterator]() {
        return this;
      },
      next(...args) {
        if (steps.length > 0) {
          let res = steps.shift()(...args);
          return {
            value: res,
            done: false
          }
        } else {
          return {
            done: true
          }
        }
      }
    }
  }
}

Task.actions.push(function task1(...args) {
  console.log("任务一:相乘") return args.reduce(function(x, y) {
    return x * y
  })
},
function task2(...args) {
  console.log("任务二:相加") return args.reduce(function(x, y) {
    return x + y
  }) * 2
},
function task3(...args) {
  console.log("任务三:相减") return args.reduce(function(x, y) {
    return x - y
  })
});

var it = Task[Symbol.iterator]();
console.log(it.next(10, 100, 2));
console.log(it.next(20, 50, 100)) console.log(it.next(10, 2, 1))
 // 
任务一:相乘 {
  "value": 2000,
  "done": false
}任务二:相加 {
  "value": 340,
  "done": false
}任务三:相减 {
  "value": 7,
  "done": false
}

3、延迟执行

假设我们有一个数据表,我们想按大小顺序依次的获取数据,但是我们又不想提前给他排序,有可能我们根本就不去使用它,所以我们可以在第一次使用的时候再排序,做到延迟执行代码:

var table = {
  "d": 1,
  "b": 4,
  "c": 12,
  "a": 12
}
table[Symbol.iterator] = function() {
  var _this = this;
  var keys = null;
  var index = 0;

  return {
    next: function() {
      if (keys === null) {
        keys = Object.keys(_this).sort();
      }

      return {
        value: keys[index],
        done: index++>keys.length
      };
    }
  }
}

for (var a of table) {
  console.log(a)
} 
// a b c d

四、结语

本章内容,重点是明白 Iterator 接口的机制,以及 for...of 循环的使用方法。

相关推荐:

ES6可迭代协议和迭代器协议详解

关于PHP聚合式迭代器接口IteratorAggregate用法分享

关于js 迭代器方法

以上がES6イテレータとfor.ofループの使い方の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。