前言
JavaScript 原有的表示“集合”的數(shù)據(jù)結(jié)構(gòu),主要是數(shù)組(Array
)和對象(Object
),ES6 又添加了Map
和Set
。用戶還可以組合使用它們,定義自己的數(shù)據(jù)結(jié)構(gòu),比如數(shù)組的成員是Map
,Map
的成員是對象。這樣就需要一種統(tǒng)一的接口機制,來處理所有不同的數(shù)據(jù)結(jié)構(gòu)。
一、Iterator介紹
遍歷器(Iterator)就是這樣一種機制。它是一種接口,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機制。任何數(shù)據(jù)結(jié)構(gòu)只要部署 Iterator 接口,就可以完成遍歷操作(即依次處理該數(shù)據(jù)結(jié)構(gòu)的所有成員)。
Iterator 的作用有三個:一是為各種數(shù)據(jù)結(jié)構(gòu),提供一個統(tǒng)一的、簡便的訪問接口;二是使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列;三是 ES6 創(chuàng)造了一種新的遍歷命令for...of
循環(huán),Iterator 接口主要供for...of
消費。
二、Iterator原理
Iterator直接翻譯是迭代器 他能夠迭代出任何可迭代數(shù)據(jù)類型中的數(shù)據(jù),為可迭代數(shù)據(jù)提供一個接口機制 能夠迭代出數(shù)據(jù);
一般情況下 :迭代數(shù)據(jù)使用函數(shù)的next方法,如果說數(shù)據(jù)存在 那么返回對象{value :數(shù)據(jù), done : false}
,
如果說 :沒有數(shù)據(jù)了 那么返回對象:{value : undefined , done : true}
所以說 我們使用iterator之前 要先定義一個指針, 指針是為了指向數(shù)據(jù)從哪開始迭代;最開始 指針的指向是第0個元素;
如下面的案例:
function makeIterator(array) {
var nextIndex = 0;
return { // 注意:此處使用了閉包,nextIndex會被一直引用,也就是會被下面一直累加;
next: function() {
// 注意:此處判斷結(jié)束條件 當(dāng)前長度是否小于數(shù)組的總長度
// 小于說明未運行結(jié)束 不小于說明改結(jié)束了
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
Iterator 的遍歷過程是這樣的:
(1)創(chuàng)建一個指針對象,指向當(dāng)前數(shù)據(jù)結(jié)構(gòu)的起始位置。也就是說,遍歷器對象本質(zhì)上,就是一個指針對象。
(2)第一次調(diào)用指針對象的next
方法,可以將指針指向數(shù)據(jù)結(jié)構(gòu)的第一個成員。
(3)第二次調(diào)用指針對象的next
方法,指針就指向數(shù)據(jù)結(jié)構(gòu)的第二個成員。
(4)不斷調(diào)用指針對象的next
方法,直到它指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置。
每一次調(diào)用next
方法,都會返回數(shù)據(jù)結(jié)構(gòu)的當(dāng)前成員的信息。具體來說,就是返回一個包含value
和done
兩個屬性的對象。其中,value
屬性是當(dāng)前成員的值,done
屬性是一個布爾值,表示遍歷是否結(jié)束。
三、實現(xiàn)Iterator接口的原生對象有
ES6 規(guī)定,默認的 Iterator 接口部署在數(shù)據(jù)結(jié)構(gòu)的Symbol.iterator屬性上;或者說,一個數(shù)據(jù)結(jié)構(gòu)只要有Symbol.iterator屬性,就認為是可遍歷的。
原生具備Iterator接口的數(shù)據(jù)結(jié)構(gòu)有:
- Array
- Map
- Set
- String
- TypedArray
- 函數(shù)的 arguments 對象
- NodeList 對象
可以看到Array原型對象和Set集合已經(jīng)實現(xiàn)了Iterator這個屬性:
// 數(shù)組
console.log("array:",Array.prototype);
// Es6新增Set集合
let set = new Set([1, 2, 3]);
console.log("set:",set);
打印如下圖:
那么數(shù)組的實例對象當(dāng)然也會擁有這個屬性,如下:
let arrIter = [1, 2, 3][Symbol.iterator]();
console.log("arrIter:", arrIter);
console.log(arrIter.next());
console.log(arrIter.next());
console.log(arrIter.next());
console.log(arrIter.next());
五、默認調(diào)用 Iterator 接口的場合
有一些場合會默認調(diào)用 Iterator 接口(即Symbol.iterator
方法),除了下文會介紹的for...of
循環(huán),還有幾個別的場合。
(1)解構(gòu)賦值
對數(shù)組和 Set 結(jié)構(gòu)進行解構(gòu)賦值時,會默認調(diào)用Symbol.iterator
方法。
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)擴展運算符
擴展運算符(…)也會調(diào)用默認的 Iterator 接口。
// 例一
var str = 'hello';
[...str] // ['h','e','l','l','o']
// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']
實際上,這提供了一種簡便機制,可以將任何部署了 Iterator 接口的數(shù)據(jù)結(jié)構(gòu),轉(zhuǎn)為數(shù)組。也就是說,只要某個數(shù)據(jù)結(jié)構(gòu)部署了 Iterator 接口,就可以對它使用擴展運算符,將其轉(zhuǎn)為數(shù)組。
let arr = [...iterable];
(3)yield*
yield*
后面跟的是一個可遍歷的結(jié)構(gòu),它會調(diào)用該結(jié)構(gòu)的遍歷器接口。
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)其他場合
由于數(shù)組的遍歷會調(diào)用遍歷器接口,所以任何接受數(shù)組作為參數(shù)的場合,其實都調(diào)用了遍歷器接口。下面是一些例子。
- for…of
- Array.from()
- Map(), Set(), WeakMap(), WeakSet()(比如
new Map([['a',1],['b',2]])
) - Promise.all()
- Promise.race()
六,for… of 循環(huán)
for... of循環(huán)其實就是 Iterator 的語法糖
for…of循環(huán)可以使用的范圍包括數(shù)組、Set 和 Map 結(jié)構(gòu)、某些類似數(shù)組的對象(比如arguments對象、DOM NodeList 對象)、字符串等。也就是說有Iterator 接口的數(shù)據(jù)類型,for …of 都可以遍歷;
(不能遍歷普通對象,會直接報錯,obj is not iterable)
數(shù)組原生具備iterator接口,(默認部署Symbol.iterator屬性),for...of 循環(huán)本質(zhì)上
就是調(diào)用這個接口產(chǎn)生的遍歷器:
for…of 使用如下:文章來源:http://www.zghlxwxcb.cn/news/detail-621565.html
## 1,遍歷數(shù)組
let arr = [{name:'Eula',age:20},{name:'Kaya',age:20}]
for (const iterator of arr) {
console.log("iterator:",iterator); // {name: 'Eula', age: 20} {name: 'Kaya', age: 20}
}
## 2,遍歷字符串
let str = "hello"
for (const iterator of str) {
console.log("iterator:",iterator); // 'h' 'e' 'l'' l'' o'
}
## 3,遍歷set集合
let set = new Set([{name:'Eula',age:20},{name:'Kaya',age:20}])
for (const iterator of set) {
console.log("iterator:",iterator); // {name: 'Eula', age: 20} {name: 'Kaya', age: 20}
}
## 4,遍歷map集合
let map = new Map()
map.set('name','Eula')
map.set('age','18')
console.log("map:",map); // Map(2) {'name' => 'Eula', 'age' => '18'}
for (const iterator of map) {
console.log("iterator:",iterator); // ['name', 'Eula'] ['age', '18']
}
如果想要遍歷普通對象可以使用 for in 循環(huán):文章來源地址http://www.zghlxwxcb.cn/news/detail-621565.html
let obj = {name:'Eula'}
for (const iterator in obj) {
console.log("鍵:",iterator); // name
console.log("值:",obj[iterator]); // Eula
}
七,總結(jié)
- 一個數(shù)據(jù)結(jié)構(gòu)的原型上只要有
Symbol.iterator
屬性,就認為是可遍歷的。 - for… of循環(huán)其實就是 Iterator 的語法糖。
到了這里,關(guān)于ES6 - Iterator迭代器和for...of 循環(huán)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!