国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > JS > 正文

ES6 迭代器(Iterator)和 for.of循環(huán)使用方法學(xué)習(xí)(總結(jié))

2024-05-06 16:42:23
字體:
供稿:網(wǎng)友

一、什么是迭代器?

生成器概念在Java,Python等語言中都是具備的,ES6也添加到了JavaScript中。Iterator可以使我們不需要初始化集合,以及索引的變量,而是使用迭代器對(duì)象的 next 方法,返回集合的下一項(xiàng)的值,偏向程序化。

迭代器是帶有特殊接口的對(duì)象。含有一個(gè)next()方法,調(diào)用返回一個(gè)包含兩個(gè)屬性的對(duì)象,分別是value和done,value表示當(dāng)前位置的值,done表示是否迭代完,當(dāng)為true的時(shí)候,調(diào)用next就無效了。

ES5中遍歷集合通常都是 for循環(huán),數(shù)組還有 forEach 方法,對(duì)象就是 for-in,ES6 中又添加了 Map 和 Set,而迭代器可以統(tǒng)一處理所有集合數(shù)據(jù)的方法。迭代器是一個(gè)接口,只要你這個(gè)數(shù)據(jù)結(jié)構(gòu)暴露了一個(gè)iterator的接口,那就可以完成迭代。ES6創(chuàng)造了一種新的遍歷命令for...of循環(huán),Iterator接口主要供for...of消費(fèi)。

二、如何使用迭代器?

1、默認(rèn) Iterator 接口

數(shù)據(jù)結(jié)構(gòu)只要部署了 Iterator 接口,我們就成這種數(shù)據(jù)結(jié)構(gòu)為“可遍歷”(Iterable)。ES6 規(guī)定,默認(rèn)的 Iterator 接口部署在數(shù)據(jù)結(jié)構(gòu)的 Symbol.iterator 屬性,或者說,一個(gè)數(shù)據(jù)結(jié)構(gòu)只要具有 Symbol.iterator 數(shù)據(jù),就可以認(rèn)為是“可遍歷的”(iterable)。

可以供 for...of 消費(fèi)的原生數(shù)據(jù)結(jié)構(gòu)

  1. Array
  2. Map
  3. Set
  4. String
  5. TypedArray(一種通用的固定長度緩沖區(qū)類型,允許讀取緩沖區(qū)中的二進(jìn)制數(shù)據(jù))
  6. 函數(shù)中的 arguments 對(duì)象
  7. NodeList 對(duì)象

可以看上面的原生數(shù)據(jù)結(jié)構(gòu)中并沒有對(duì)象(Object),為什么呢?

那是因?yàn)閷?duì)象屬性的遍歷先后順序是不確定的,需要開發(fā)者手動(dòng)指定。本質(zhì)上,遍歷器是一種線性處理,對(duì)于任何非線性的數(shù)據(jù)結(jié)構(gòu),部署遍歷器接口就等于部署一種線性變換。

做如下處理,可以使對(duì)象供 for...of 消費(fèi):

// code1function 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、調(diào)用 Iterator 接口的場合

(1) 解構(gòu)賦值

// code2let 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) 擴(kuò)展運(yùn)算符

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

(3)Generator 函數(shù)中的 yield* 表達(dá)式(下一章介紹)

// code4let 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)其它場合

  1. for..of
  2. Array.from
  3. Map()、Set()、WeakMap()、WeakSet()
  4. Promise.all()
  5. Promise.race()

3、for...of 循環(huán)的優(yōu)勢

先看看,數(shù)組 forEach 方法的缺點(diǎn):

// code5myArray.forEach(function (value) { console.log(value);});

這個(gè)寫法的問題在于,無法中途跳出 forEach 循環(huán),break 命令或 return 命令都不能生效。

再看看,對(duì)象 for...in 的循環(huán)的缺點(diǎn):

for (var index in myArray) { console.log(myArray[index]);};
  1. 數(shù)組的鍵名是數(shù)字,但是 for...in 循環(huán)是以字符串作為鍵名,“0”、“1”、“2”等。
  2. for...in 循環(huán)不僅可以遍歷數(shù)字鍵名,還會(huì)遍歷手動(dòng)添加的期推薦,甚至包括原型鏈上的鍵。
  3. 某些情況下,for...in 循環(huán)會(huì)議任意順序遍歷鍵名
  4. for...in 遍歷主要是為遍歷對(duì)象而設(shè)計(jì)的,不適用于遍歷數(shù)組

那么,for...of 有哪些顯著的優(yōu)點(diǎn)呢?

  1. 有著同 for...in 一樣的簡潔語法,但是沒有 for...in 那些缺點(diǎn)
  2. 不同于 forEach 方法,它可以與 break、continue 和 return 配合使用
  3. 提供了遍歷所有數(shù)據(jù)結(jié)構(gòu)的統(tǒng)一操作接口
for (var n of fibonacci) { if (n > 1000) {  break;  console.log(n); }}

4、各數(shù)據(jù)類型如何使用 for...of 循環(huán)?

(1)數(shù)組

for...of 循環(huán)允許遍歷數(shù)組獲得鍵值

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 循環(huán)調(diào)用遍歷器接口,數(shù)組的遍歷器接口只返回具有數(shù)字索引的值

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 結(jié)構(gòu)

var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);for (var e of engines) {  console.log(e);}// Gecko// Trident// Webkitvar 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 循環(huán)遍歷Map 和 Set 結(jié)構(gòu)時(shí),遍歷的順序是按照各個(gè)成員被添加進(jìn)數(shù)據(jù)結(jié)構(gòu)的順序,Set 結(jié)構(gòu)遍歷時(shí)返回的是一個(gè)值,而 Map 結(jié)構(gòu)遍歷時(shí)返回的是一個(gè)數(shù)組,該數(shù)組的兩個(gè)成員分別為當(dāng)前 Map 成員的鍵名和鍵值。

(3)類數(shù)組對(duì)象

字符串

// 普通的字符串遍歷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 對(duì)象

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

arguments 對(duì)象

function printArgs() { for (let x of arguments) {  console.log(x); }}printArgs("a", "n");// "a"// "n"

沒有 Iterator 接口類數(shù)組對(duì)象的遍歷處理

借用 Array.from 方法處理

let arrayLike = {  length: 2,  0 : 'a',  1 : 'b'};// 報(bào)錯(cuò)for (let x of arrayLike) {  console.log(x);}// 正確for (let x of Array.from(arrayLike)) {  console.log(x);}

(4)對(duì)象

對(duì)于普通對(duì)象,不能直接使用 for...of 遍歷,否則會(huì)報(bào)錯(cuò),必須部署了 Iterator 接口才能使用。如下兩種方法部署:

// 方法一:使用 Object.keys 方法講對(duì)象的鍵名生成一個(gè)數(shù)組for (var key of Object.keys(someObject)) { console.log(key + ": " + someObject[key]);}// 方法二:使用Generator 函數(shù)將對(duì)象重新包裝一下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

三、迭代器應(yīng)用實(shí)例

1、斐波那契數(shù)列

下面我們就使用迭代器來自定義自己的一個(gè)斐波那契數(shù)列組,我們直到斐波那契數(shù)列有兩個(gè)運(yùn)行前提,第一個(gè)前提是初始化的前兩個(gè)數(shù)字為0,1,第二個(gè)前提是將來的每一個(gè)值都是前兩個(gè)值的和。這樣我們的目標(biāo)就是每次都迭代輸出一個(gè)新的值。

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、任務(wù)隊(duì)列迭代器

我們可以定義一個(gè)任務(wù)隊(duì)列,該隊(duì)列初始化時(shí)為空,我們將待處理的任務(wù)傳遞后,傳入數(shù)據(jù)進(jìn)行處理。這樣第一次傳遞的數(shù)據(jù)只會(huì)被任務(wù)1處理,第二次傳遞的只會(huì)被任務(wù)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("任務(wù)一:相乘") return args.reduce(function(x, y) {    return x * y  })},function task2(...args) {  console.log("任務(wù)二:相加") return args.reduce(function(x, y) {    return x + y  }) * 2},function task3(...args) {  console.log("任務(wù)三:相減") 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)) // 任務(wù)一:相乘 {  "value": 2000,  "done": false}任務(wù)二:相加 {  "value": 340,  "done": false}任務(wù)三:相減 {  "value": 7,  "done": false}

3、延遲執(zhí)行

假設(shè)我們有一個(gè)數(shù)據(jù)表,我們想按大小順序依次的獲取數(shù)據(jù),但是我們又不想提前給他排序,有可能我們根本就不去使用它,所以我們可以在第一次使用的時(shí)候再排序,做到延遲執(zhí)行代碼:

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

四、結(jié)語

本章內(nèi)容,重點(diǎn)是明白 Iterator 接口的機(jī)制,以及 for...of 循環(huán)的使用方法。下一章介紹生成器函數(shù) Generator 函數(shù)。

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持VeVb武林網(wǎng)。


注:相關(guān)教程知識(shí)閱讀請移步到JavaScript/Ajax教程頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 保康县| 沙坪坝区| 庄浪县| 准格尔旗| 长海县| 普陀区| 安达市| 文水县| 昔阳县| 通辽市| 营口市| 新干县| 资源县| 彭山县| 墨竹工卡县| 阿图什市| 天镇县| 玛纳斯县| 邢台市| 汤原县| 柘荣县| 宝应县| 台湾省| 东方市| 莱阳市| 平顺县| 汪清县| 铜鼓县| 竹山县| 双柏县| 阿克| 武宁县| 陇南市| 化隆| 澄江县| 福贡县| 浮山县| 汝州市| 裕民县| 河津市| 孟村|