這篇文章主要介紹了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 消費的原生資料結構
Array
- ##Map
- #Set
- String
- #TypedArray(一種通用的固定長度緩衝區類型,允許讀取緩衝區中的二進位資料)
- 函數中的arguments 物件
- NodeList 物件
##可以看上面的原生資料結構中並沒有物件(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 | 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);
}
|
登入後複製
#2、呼叫Iterator 介面的場合
(1)解構賦值
1 2 3 4 5 6 | let set = new Set().add('a').add('b').add('c');
let [x,y] = set;
let [first, ...rest] = set;
|
登入後複製
(2) 擴充運算子
1 2 3 4 5 6 7 8 | var str = 'hello';
[...str]
let arr = ['b', 'c'];
['a', ...arr, 'd']
|
登入後複製
(3)Generator 函數中的yield* 表達式(下一章介紹)
1 2 3 4 5 6 7 8 9 10 11 12 13 | let generator = function * () {
yield 1;
yield* [2,3,4];
yield 5;
};
var iterator = generator();
iterator.next()
iterator.next()
iterator.next()
iterator.next()
iterator.next()
iterator.next()
|
登入後複製
(4)其它場合
for..of Array.from Map()、Set( )、WeakMap()、WeakSet() Promise.all() Promise.race()
#3、for...of 迴圈的優勢
先看看,陣列forEach 方法的缺點:
1 2 3 4 | 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」等。 for...in 迴圈不僅可以遍歷數字鍵名,還會遍歷手動新增的期推薦,甚至包含原型鏈上的按鍵。 某些情況下,for...in 迴圈會議任意順序遍歷鍵名 for...in 遍歷主要是為遍歷物件而設計的,不適用於遍歷陣列 那麼,for...of 有哪些顯著的優點呢?
有著同for...in 一樣的簡潔語法,但是沒有for...in 那些缺點 不同於forEach 方法,它可以與break、continue 和return 配合使用 提供了遍歷所有資料結構的統一操作介面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);
}
for (let a of arr) {
console.log(a);
}
|
登入後複製
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);
}
for (let i of arr) {
console.log(i);
}
|
登入後複製
(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);
}
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);
}
|
登入後複製
由上述的程式碼可以看出,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);
}
for (let x of 'a\uD83D\uDC0A') {
console.log(x);
}
|
登入後複製
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");
|
登入後複製
沒有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 | for ( var key of Object.keys(someObject)) {
console.log(key + ": " + someObject[key]);
}
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);
}
|
登入後複製
三、迭代器应用实例
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)
}
|
登入後複製
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
在vue中如何实现微信分享朋友圈,发送朋友
详解如何实现vuex(详细教程)
通过vue.js实现微信支付
以上是ES6 迭代器和 for.of迴圈(詳細教學)的詳細內容。更多資訊請關注PHP中文網其他相關文章!