簡(jiǎn)單的復(fù)習(xí)下 for...of 循環(huán)的使用方法

基本使用
// 遍歷數(shù)組let array = ['a', 'b', 'c'];for (let value of array) {console.log(value); // 分別打印 'a' 'b' 'c'}// 遍歷字符串let str = "abc";for (let value of str) {console.log(value); // 分別打印 'a' 'b' 'c'}// 遍歷Maplet map = new Map([["a", 1], ["b", 2], ["c", 3]]);for (let value of map) {console.log(value); // 分別打印 ["a", 1] ["b", 2] ["c", 3]}// 遍歷Setlet set = new Set(['a', 'a', 'b', 'c', 'b', 'c']);for (let value of set) {console.log(value); // 分別打印 'a' 'b' 'c'}// 遍歷arguments(function() {for (let argument of arguments) {console.log(argument); // 分別打印 'a' 'b' 'c'}})('a', 'b', 'c');
可迭代對(duì)象
for…of的語(yǔ)法比較簡(jiǎn)單,上面我們遍歷了這么多數(shù)據(jù),現(xiàn)在我們使用for…of遍歷一下對(duì)象:
let object = {a: 1,b: 2,c: 3,}for (let value of object) {console.log(value); // 報(bào)錯(cuò):Uncaught TypeError: object is not iterable}
結(jié)果很不幸,使用for…of遍歷對(duì)象報(bào)錯(cuò)了。為什么報(bào)錯(cuò)了,報(bào)錯(cuò)的錯(cuò)誤提示寫的很清楚,因?yàn)閛bject對(duì)象不是可迭代的,也就是說(shuō)它不是可迭代對(duì)象。
這里遇到一個(gè)新的名詞,什么是可迭代對(duì)象呢?
要成為可迭代對(duì)象, 這個(gè)對(duì)象必須實(shí)現(xiàn)@@iterator方法,并且該方法返回一個(gè)符合迭代器協(xié)議的對(duì)象。
這里有2個(gè)問(wèn)題,第一怎么去實(shí)現(xiàn)一個(gè)@@iterator方法?看到@@xxx這樣的方法,想都不用想就是指[Symbol.xxx]方法,這里也就是一個(gè)方法的key是[Symbol.iterator]就可以了,比如:
let object = {a: 1,b: 2,c: 3,[Symbol.iterator]: function() {}}
第二個(gè)問(wèn)題什么是符合迭代器協(xié)議的對(duì)象?首先迭代器協(xié)議的對(duì)象是一個(gè)對(duì)象,這個(gè)對(duì)象有一個(gè)next方法,這個(gè)next方法每次調(diào)用有會(huì)返回一個(gè)對(duì)象,這個(gè)返回的對(duì)象又有一個(gè)done屬性和一個(gè)value屬性。
其中done屬性表示是否完成,如果是true則表示完成,false或者不寫則表示沒(méi)有完成;value表示值,也就是for…of循環(huán)時(shí)每次使用的值,如果done為true時(shí)候則可以不寫。舉個(gè)可迭代對(duì)象的例子:
let loop10 = {[Symbol.iterator]: function() {let i = 0return {next: function() {return {value: i++,done: i > 10}}}}}for (let value of loop10) {console.log(value); // 分別打印 0 1 2 3 4 5 6 7 8 9}
迭代器協(xié)議的對(duì)象也可以自己調(diào)用著玩玩:
let iterator = loop10[Symbol.iterator]();iterator.next(); // 返回 {value: 0, done: false}iterator.next(); // 返回 {value: 1, done: false}iterator.next(); // 返回 {value: 2, done: false}
當(dāng)然迭代器協(xié)議的對(duì)象不僅僅只能用在for-of循環(huán)中,也可以用在數(shù)組的解構(gòu)上:
let arr = [...loop10]; // arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
可迭代對(duì)象與generator函數(shù)
當(dāng)我們看到一個(gè)個(gè)可迭代對(duì)象的next方法,再看看一個(gè)個(gè)的{value: 0, done: false}這種符合迭代器協(xié)議的對(duì)象,這時(shí)不想跟generator沒(méi)點(diǎn)關(guān)系都不行了,沒(méi)錯(cuò)generator函數(shù)返回的正是可迭代對(duì)象。
我們先使用常規(guī)方法實(shí)現(xiàn)一下對(duì)象的for…of遍歷。
let object = {a: 1,b: 2,c: 3,[Symbol.iterator]: function() {let keys = Object.keys(this);let i = 0return {next: function() {return {value: keys[i++],done: i > keys.length}}}}}for (let value of object) {console.log(value); // 分別打印 'a' 'b' 'c'}
使用generator函數(shù)可以簡(jiǎn)化上述步驟:
let object = {a: 1,b: 2,c: 3,[Symbol.iterator]: function *() {let keys = Object.keys(this)for (let i = 0; i < keys.length; i++) {yield keys[i]}}}for (let value of object) {console.log(value); // 分別打印 'a' 'b' 'c'}
是不是很方便?這里偷偷告訴你一個(gè)小秘密:generator函數(shù)調(diào)用后的對(duì)象也可以用在for…of上。
let loop10Gen = function *() {for (let i = 0; i < 10; i++) {yield i}}// 注意這里是loop10Gen() 而不是loop10Genfor (const value of loop10Gen()) {console.log(value); // 分別打印 0 1 2 3 4 5 6 7 8 9}
上面不是說(shuō)了,可迭代對(duì)象要實(shí)現(xiàn)一個(gè)@@iterator方法,這里實(shí)現(xiàn)了嗎?沒(méi)錯(cuò),這里還真實(shí)現(xiàn)了!你可以試試:
let itarator = loop10Gen();itarator[Symbol.iterator]() === itarator; // 返回true
于是我們就得到一個(gè)比較繞的真理:generator調(diào)用后的對(duì)象,既是可迭代對(duì)象,也是符合迭代器協(xié)議的對(duì)象。
for…of與for…in的區(qū)別
for…in遍歷的是對(duì)象的可枚舉屬性,而for…of語(yǔ)句遍歷的是可迭代對(duì)象所定義要迭代的數(shù)據(jù)。
由于for…in遍歷的是對(duì)象的可枚舉屬性,所以對(duì)于數(shù)組來(lái)說(shuō)打印的是鍵,而不是值:
let array = ['a', 'b', 'c'];for (const value in array) {console.log(value); // 分別打印 '0' '1' '2'}for (const value of array) {console.log(value); // 分別打印 'a' 'b' 'c'}
for…in會(huì)遍歷對(duì)象原型和原型鏈上的可枚舉的屬性。
let array = ['a', 'b', 'c'];Object.prototype.formObject = true;Array.prototype.formArray = true;array.hello = 'world'for (const value in array) {console.log(value); // 分別打印 0 1 2 hello formArray formObject}for (const value of array) {console.log(value); // 分別打印 'a' 'b' 'c'}
通常為了避免for…in遍歷原型和原型鏈上無(wú)關(guān)的可枚舉屬性,使用Object.hasOwnProperty()方法來(lái)判斷:
let array = ['a', 'b', 'c'];Object.prototype.formObject = true;Array.prototype.formArray = true;array.hello = 'world'for (const value in array) {if (Object.hasOwnProperty.call(array, value)) {console.log(value); // 分別打印 0 1 2 hello}}for (const value of array) {console.log(value); // 分別打印 'a' 'b' 'c'}
可迭代對(duì)象的return方法
可迭代對(duì)象除了next方法外還有return方法,主要用在循環(huán)中斷的時(shí)候會(huì)調(diào)用,比如是用break關(guān)鍵字、或者拋出一個(gè)Error:
let loop10 = {[Symbol.iterator]: function() {let i = 0return {next: function() {return {value: i++,done: i > 10}},return: function() {console.log('return調(diào)用了~~')return { done: true };}}}}for (let value of loop10) {console.log(value); // 分別打印 0 1 2 3if (value === 3) {break; // 打印 'return調(diào)用了~~'}}
本文完~
學(xué)習(xí)更多技能
請(qǐng)點(diǎn)擊下方公眾號(hào)
![]()
