首頁 web前端 js教程 ES6 迭代器和 for.of迴圈(詳細教學)

ES6 迭代器和 for.of迴圈(詳細教學)

Jun 06, 2018 pm 05:34 PM
es6 迭代器

這篇文章主要介紹了ES6 迭代器(Iterator)和 for.of循環使用方法學習總結,現在分享給大家,也給大家做個參考。

一、什麼是迭代器?

生成器概念在Java,Python等語言中都是具備的,ES6也加入了JavaScript中。 Iterator可以讓我們不需要初始化集合,以及索引的變量,而是使用迭代器物件的 next 方法,傳回集合的下一項的值,偏向程式化。

迭代器是帶有特殊介面的物件。含有一個next()方法,呼叫傳回一個包含兩個屬性的對象,分別是value和done,value表示目前位置的值,done表示是否迭代完,當為true的時候,呼叫next就無效了。

ES5中遍歷集合通常都是 for循環,陣列還有 forEach 方法,物件就是 for-in,ES6 中又加入了 Map 和 Set,而迭代器可以統一處理所有集合資料的方法。迭代器是一個接口,只要你這個資料結構暴露了一個iterator的接口,那就可以完成迭代。 ES6創造了一個新的遍歷指令for...of循環,Iterator介面主要供for...of消費。

二、如何使用迭代器?

1、預設Iterator 接口

#資料結構只要部署了Iterator 接口,我們就成這種資料結構為「可遍歷」( Iterable)。 ES6 規定,預設的 Iterator 介面部署在資料結構的 Symbol.iterator 屬性,或者說,一個資料結構只要具有 Symbol.iterator 數據,就可以認為是「可遍歷的」(iterable)。

可以讓for...of 消費的原生資料結構

  1. Array

  2. ##Map

  3. #Set

  4. String

  5. #TypedArray(一種通用的固定長度緩衝區類型,允許讀取緩衝區中的二進位資料)

  6. 函數中的arguments 物件

  7. NodeList 物件

  8. ##可以看上面的原生資料結構中並沒有物件(Object),為什麼呢?

那是因為物件屬性的遍歷先後順序是不確定的,需要開發者手動指定。本質上,遍歷器是一種線性處理,對於任何非線性的資料結構,部署遍歷器介面就等於部署一種線性變換。

做如下處理,可以讓物件供for...of 消耗:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

// 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)解構賦值

1

2

3

4

5

6

// 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) 擴充運算子

1

2

3

4

5

6

7

8

// code3

// 例一

var str = 'hello';

[...str] // ['h','e','l','l','o']

// 例二

let arr = ['b', 'c'];

['a', ...arr, 'd']

// ['a', 'b', 'c', 'd']

登入後複製

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

1

2

3

4

5

6

7

8

9

10

11

12

13

// 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
  1. Array.from
  2. Map()、Set( )、WeakMap()、WeakSet()
  3. Promise.all()
  4. Promise.race()
#3、for...of 迴圈的優勢

先看看,陣列forEach 方法的缺點:

1

2

3

4

// code5

myArray.forEach(function (value) {

 console.log(value);

});

登入後複製

這個寫法的問題在於,無法中途跳出forEach 循環,break 指令或return 指令都不能生效。

再看看,物件for...in 的迴圈的缺點:

1

2

3

for (var index in myArray) {

 console.log(myArray[index]);

};

登入後複製

    陣列的鍵名是數字,但是for...in 迴圈是以字串作為鍵名,「0」、「1」、「2」等。
  1. for...in 迴圈不僅可以遍歷數字鍵名,還會遍歷手動新增的期推薦,甚至包含原型鏈上的按鍵。
  2. 某些情況下,for...in 迴圈會議任意順序遍歷鍵名
  3. for...in 遍歷主要是為遍歷物件而設計的,不適用於遍歷陣列
  4. 那麼,for...of 有哪些顯著的優點呢?

    有著同for...in 一樣的簡潔語法,但是沒有for...in 那些缺點
  1. 不同於forEach 方法,它可以與break、continue 和return 配合使用
  2. 提供了遍歷所有資料結構的統一操作介面
  3. 1

    2

    3

    4

    5

    6

    for (var n of fibonacci) {

     if (n > 1000) {

      break;

      console.log(n);

     }

    }

    登入後複製
4、各資料型別如何使用for...of 迴圈?

(1)數組

for...of 循環允許遍歷數組獲得鍵值

1

2

3

4

5

6

7

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 循環調用遍歷器接口,數組的遍歷器介面只傳回具有數字索引的值

1

2

3

4

5

6

7

8

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 結構

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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 結構遍歷時傳回的是一個數組,該數組的兩個成員分別為目前Map 成員的鍵名和鍵值。

(3)類別陣列物件

字串

1

2

3

4

5

6

7

8

9

10

11

12

// 普通的字符串遍历

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 物件

1

2

3

4

let paras = document.querySelectorAll("p");

for (let p of paras) {

 p.classList.add("test");

}

登入後複製

arguments 物件

1

2

3

4

5

6

7

8

function printArgs() {

 for (let x of arguments) {

  console.log(x);

 }

}

printArgs("a", "n");

// "a"

// "n"

登入後複製

沒有Iterator 介面類別陣列物件的遍歷處理

借用Array.from 方法處理

1

2

3

4

5

6

7

8

9

10

11

12

13

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 介面才能使用。如下兩種方法部署:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

// 方法一:使用 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

登入後複製

三、迭代器应用实例

1、斐波那契数列

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

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处理… 代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

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、延迟执行

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

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

登入後複製

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在vue中如何实现微信分享朋友圈,发送朋友

详解如何实现vuex(详细教程)

通过vue.js实现微信支付

以上是ES6 迭代器和 for.of迴圈(詳細教學)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱門文章

倉庫:如何復興隊友
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱門文章

倉庫:如何復興隊友
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱門文章標籤

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

C#中如何使用迭代器和遞歸演算法處理數據 C#中如何使用迭代器和遞歸演算法處理數據 Oct 08, 2023 pm 07:21 PM

C#中如何使用迭代器和遞歸演算法處理數據

async是es6還是es7的 async是es6還是es7的 Jan 29, 2023 pm 05:36 PM

async是es6還是es7的

小程式為什麼要將es6轉es5 小程式為什麼要將es6轉es5 Nov 21, 2022 pm 06:15 PM

小程式為什麼要將es6轉es5

PHP程式中的迭代器最佳實踐 PHP程式中的迭代器最佳實踐 Jun 06, 2023 am 08:05 AM

PHP程式中的迭代器最佳實踐

Golang迭代器實作及使用詳解 Golang迭代器實作及使用詳解 Mar 17, 2024 pm 09:21 PM

Golang迭代器實作及使用詳解

es6暫時性死區是什麼意思 es6暫時性死區是什麼意思 Jan 03, 2023 pm 03:56 PM

es6暫時性死區是什麼意思

es5和es6怎麼實現陣列去重 es5和es6怎麼實現陣列去重 Jan 16, 2023 pm 05:09 PM

es5和es6怎麼實現陣列去重

Python中的主要和次要提示 Python中的主要和次要提示 Aug 25, 2023 pm 04:05 PM

Python中的主要和次要提示

See all articles