1.什么是Iterator ?
遍歷器(Iterator)它是一種接口,為各種不同的數據結構提供統一的訪(fǎng)問(wèn)機制。任何數據結構只要部署 Iterator 接口,就可以完成遍歷操作。
Iterator 的作用有三個(gè):
1.是為各種數據結構,提供一個(gè)統一的、簡(jiǎn)便的訪(fǎng)問(wèn)接口;
2.是使得數據結構的成員能夠按某種次序排列;
3.是 ES6 創(chuàng )造了一種新的遍歷命令for...of循環(huán),Iterator 接口主要供for...of消費。
2.Iterator怎么用 ?
for...of語(yǔ)句在可迭代對象(包括 Array,Map,Set,String,TypedArray,arguments 對象等等)上創(chuàng )建一個(gè)迭代循環(huán),調用自定義迭代鉤子,并為每個(gè)不同屬性的值執行語(yǔ)句
/**
* 語(yǔ)法
* @variable 每次迭代中屬性的值
* @iterable 被迭代枚舉其屬性的對象
*/
for (variable of iterable) {
//statements
}
const array1 = ['a', 'b', 'c']; for (const element of array1) { console.log(element); } // a // b // c
接下來(lái)一起來(lái)實(shí)踐一下上面那些類(lèi)型是不是真的可以用


// Array let arr = [10, 20, 30]; for (let value of arr) { value += 1; console.log(value); } // 11 // 21 // 31 // Map let map = new Map([["a", 1], ["b", 2], ["c", 3]]); for (let entry of map) { console.log(entry); } // ["a", 1] // ["b", 2] // ["c", 3] // Set let set = new Set([1, 1, 2, 2, 3, 3]); for (let value of set) { console.log(value); } // 1 // 2 // 3 // String let str = "boo"; for (let value of str) { console.log(value); } // "b" // "o" // "o" // TypedArray let typedArr = new Uint8Array([0x00, 0xff]); for (let value of typedArr) { console.log(value); } // 0 // 255 // arguments (function() { for (let argument of arguments) { console.log(argument); } })(1, 2, 3); // 1 // 2 // 3
那其他的類(lèi)型用for of會(huì )怎么樣?
比如一個(gè)普通的object類(lèi)型

產(chǎn)品:“那我就要把for of用在對象上”
我:“對象不能用for of,你看這不都報錯了嘛”
產(chǎn)品:“不聽(tīng)不聽(tīng),你是不是不想做”
我:“好吧好吧,既然你想要用,那就滿(mǎn)足你”
首先要了解遍歷器 Iterator 的協(xié)議,傳送門(mén):MDN 迭代協(xié)議,
簡(jiǎn)而言之就是:
迭代協(xié)議具體分為兩個(gè)協(xié)議:可迭代協(xié)議和迭代器協(xié)議。
可迭代協(xié)議允許 JavaScript 對象定義或定制它們的迭代行為,例如,在一個(gè) for..of 結構中,哪些值可以被遍歷到。一些內置類(lèi)型同時(shí)是內置可迭代對象,并且有默認的迭代行為,比如 Array 或者 Map,而其他內置類(lèi)型則不是(比如 Object))。
要成為可迭代對象, 一個(gè)對象必須實(shí)現 @@iterator 方法。這意味著(zhù)對象(或者它原型鏈上的某個(gè)對象)必須有一個(gè)鍵為 @@iterator 的屬性,可通過(guò)常量 Symbol.iterator 訪(fǎng)問(wèn)該屬性:
迭代器協(xié)議定義了產(chǎn)生一系列值(無(wú)論是有限個(gè)還是無(wú)限個(gè))的標準方式。當值為有限個(gè)時(shí),所有的值都被迭代完畢后,則會(huì )返回一個(gè)默認返回值。
只有實(shí)現了一個(gè)擁有以下語(yǔ)義(semantic)的 next() 方法,一個(gè)對象才能成為迭代器:
var obj = {
data: [1,2,3],
[Symbol.iterator]() {
var nextIndex = 0, self = this;
return {
next() {
var done = nextIndex >= self.data.length;
var value = done ? undefined : self.data[nextIndex++]
return { value: value, done: done }
}
}
}
}
for (const item of obj) { console.log(item) }
// 1
// 2
// 3
由此可見(jiàn),普通對象不可直接使用for of,在[Symbol.iterator]屬性上部署遍歷器生成的方法后即可被for of使用(原型鏈上的對象具有該方法也可)
實(shí)際上,對象之所以沒(méi)有默認部署 Iterator 接口,是因為對象的哪個(gè)屬性先遍歷,哪個(gè)屬性后遍歷是不確定的,需要開(kāi)發(fā)者手動(dòng)指定。
那知道了上面遍歷器的協(xié)議,我們可以通過(guò)更簡(jiǎn)單的方式判定上述六種類(lèi)型是否實(shí)現了遍歷器接口了
Array.prototype.hasOwnProperty(Symbol.iterator); // true Set.prototype.hasOwnProperty(Symbol.iterator); // true Map.prototype.hasOwnProperty(Symbol.iterator); // true String.prototype.hasOwnProperty(Symbol.iterator); // true (function(){ console.log(arguments.hasOwnProperty(Symbol.iterator)) })(1,2,3) // true Uint8Array.prototype.__proto__.hasOwnProperty(Symbol.iterator) // true
3. 與for in的區別
可能看到for of 有人想到for in,那這兩個(gè)長(cháng)這么像,他兩有啥區別呢。咱們一起來(lái)看看
var arr1 = ['a', 'b', 'c', 'd']; // 輸出鍵名 for (let a in arr1) { console.log(a); // 0 1 2 3 }
// 輸出鍵值 for (let a of arr1) { console.log(a); // a b c d }
// 注意:還有個(gè)細節
// for...of循環(huán)調用遍歷器接口,數組的遍歷器接口只返回具有數字索引的屬性。這一點(diǎn)跟for...in循環(huán)也不一樣。
let arr2 = [3, 5, 7];
arr2.foo = 'hello';
for (let i in arr2) {
console.log(i); // "0", "1", "2", "foo"
}
for (let i of arr2) {
console.log(i); // "3", "5", "7"
}
var arr = Array(10).fill(1);
// for i 循環(huán)法,不夠簡(jiǎn)潔
for (var i = 0; i < arr.length; i++) {
console.log(arr[i])
}
// forEach 循環(huán),不能手動(dòng)跳出循環(huán)
arr.forEach(function (value) {
console.log(value);
})
// for i 循環(huán)獲取的是鍵名,而不是鍵值,因此不大適用于遍歷數組
for (var i in arr) {
console.log(i)
}
// for of 可以跳出循環(huán),語(yǔ)法簡(jiǎn)潔
for (var i of arr) {
console.log(i)
}
經(jīng)比較,遍歷對象用for in, 遍歷數組用for of
實(shí)際上,要實(shí)現對象的Iterator接口還有更簡(jiǎn)潔的方法,就是使用generator
let obj = { * [Symbol.iterator]() { yield 'hello'; yield 'world'; } }; for (let x of obj) { console.log(x); } // hello // world
看到這里有人又奇怪了,這個(gè) * 號是個(gè)什么鬼?
答案是 Generator 函數,Generator 函數是一個(gè)普通函數,但是有兩個(gè)特征。一是,function關(guān)鍵字與函數名之間有一個(gè)星號;二是,函數體內部使用yield表達式,定義不同的內部狀態(tài)(yield在英語(yǔ)里的意思就是“產(chǎn)出”)。
2020-06-09 11:23:51
聯(lián)系客服