遍歷器Iterator就是一種機制,是一種接口,能夠爲不一樣的數據結構提供統一的訪問機制
for...of循環
,Iterator接口主要就是供for...of循環使用的!
返回的是一個對象,對象有兩個屬性,value和done兩個屬性。value是當前成員的值,done是一個布爾值,表示遍歷是否結束
另外done:false和vallue:undefined是能夠省略的,由於不返回done屬性那麼確定就是undefined被判斷爲false,不返回value屬性那麼確定就是undefined
須要注意的是,在原生的iterator接口實現中,對象是不能夠實現iterator接口的,也就是for...of循環會失敗
// 1. 對於對象來講,只能遍歷key爲索引的鍵值 function makeIterator(obj){ var index=0; return { next:function(){ return { done:obj[index]?true:false, value:obj[index++] } } } } var obj={0:'w',1:444,5:555,a:'yy'} var res1=makeIterator(obj) console.log(res1.next());//{done: true, value: "w"} console.log(res1.next());//{done: true, value: 444} console.log(res1.next());//{done: false, value: undefined} console.log(res1.next());//{done: false, value: undefined} // 而且注意:索引爲數字也不必定能遍歷到,由於要根據index的值,若是要遍歷到5,那麼須要繼續調用兩次next // 1.2 可是不重寫iterator接口(如上使用函數,或者重寫Symbol.iterator接口)的話 // 對 對象使用for..of循環會報錯! for(var item of {0:1,1:22}){ console.log(item);//Uncaught TypeError: {(intermediate value)(intermediate value)} is not iterable } // 2. 對於數組來講 function makeArray(arr){ var index=0; return { next:function(){ return { done:arr[index]?true:false, value:arr[index++] } } } } var res2=makeArray([5,4,3]) console.log(res2.next())//{done: true, value: 5} console.log(res2.next())//{done: true, value: 4} console.log(res2.next())//{done: true, value: 3} console.log(res2.next())//{done: false, value: undefined} 複製代碼
Iterator接口的目的即便爲全部的數據結構,提供統一的訪問機制,即for...of循環
可遍歷的iterable
Symbol.iterator屬性上
,因此咱們能夠經過判斷是否具備Symbol.iterator屬性做爲可遍歷的依據
通常獲取Symbol.iterator屬性都是使用[Symbol.iterator]的形式,由於Symbol.iterator至關於一個表示式,相似[1+'2']使用[]形式
// 1. 數組 var arr=[] console.log(arr[(Symbol.iterator)])//ƒ values() { [native code] } // 2. Map var map=new Map() console.log(map[Symbol.iterator]);//ƒ entries() { [native code] } // 3. Set var set=new Set() console.log(set[Symbol.iterator]);//ƒ values() { [native code] } // 4. 對象(沒有iterator接口) var obj={} console.log(obj[Symbol.iterator]);//undeined // 5. 對象添加數字屬性仍是沒有terator接口的,除非直接修改對象的Symbol.iterator接口 var obj2={0:1,1:222} console.log(obj2[Symbol.iterator]);//undeined // 6. 字符串也有iterator接口 var str="hello world" console.log(str[Symbol.iterator]);//ƒ [Symbol.iterator]() { [native code] } // 7. 數字沒有 var nums=1234 console.log(nums[Symbol.iterator]);//undeined // 8. arguments,函數參數數組有! function func(){ console.log(arguments[Symbol.iterator]);//ƒ values() { [native code] } } func(1,4,8) // 9. Nodeslist 節點列表有! console.log(document.getElementsByClassName("one")[Symbol.iterator]);//ƒ values() { [native code] } 複製代碼
Array,String,Nodelist,arguments,Map,Set
for...of循環會自動遍歷他們
在Symbol.iterator屬性上部署,這樣纔會被for...of循環遍歷到
var arr=[4,6,8] // 1. for...of for(var item of arr){ console.log(item); } // 2. 調用Symbol.iterator接口方法 var res=arr[Symbol.iterator]() console.log(res.next())//{value: 4, done: false} console.log(res.next())//{value: 6, done: false} console.log(res.next());//{value: 8, done: false} console.log(res.next());//{value: undefined, done: true} 複製代碼
對象沒有部署iterator接口的緣由:對象的哪一個屬性先遍歷,哪一個屬性後遍歷是不肯定的。
class RangeIterator { constructor(start, stop) { this.value = start; this.stop = stop; } [Symbol.iterator]() { console.log("調用Symbol.iterator屬性") return this; } next() { console.log("調用next屬性") var value = this.value; if (value < this.stop) { this.value++; return {done: false, value: value}; } return {done: true, value: undefined}; } } function range(start, stop) { return new RangeIterator(start, stop); } // 獲得一個對象 console.log(range(0, 3));//RangeIterator {value: 0, stop: 3} // 而後for...of遍歷該對象就至關於一直調用該對象的next方法,直到done爲true console.log(range(0, 3).__proto__); /* 該實例的原型具備如下屬性 constructor: class RangeIterator next: ƒ next() Symbol(Symbol.iterator): ƒ [Symbol.iterator]() __proto__: Object */ for (var value of range(0, 3)) { console.log(value); // 0, 1, 2 } 複製代碼
咱們實際上給對象部署ierator接口能夠直接調用數組的iterator屬性
並且必須加上length屬性才能在for...of循環獲取到值
類數組對象指的就是具備length屬性的對象,因此length屬性必須有,決定着循環的次數
給對象的Symbol.iterator屬性設置爲數組的Symbol.iterator屬性。[Symbol.iterator]:Array.prototype[Symbol.iterator]
// 1. 對象具備0,1,2這些數字屬性 var obj1={ 0:'a', 1:3333, 2:'w', length:3, [Symbol.iterator]:Array.prototype[Symbol.iterator] } for(var item of obj1){ console.log(item);//a,3333,w } // 2. 沒有數字屬性 var obj2={ length:3, [Symbol.iterator]:Array.prototype[Symbol.iterator] } for(var item of obj2){ console.log(item);//undefined,undefined,undefined } // 3. 沒有length屬性(此時啥都沒打印。) var obj3={ [Symbol.iterator]:Array.prototype[Symbol.iterator] } for(var item of obj3){ console.log(item); } // 4. 具備非數字屬性,且無length屬性(不打印。) var obj4={ a:'a', [Symbol.iterator]:Array.prototype[Symbol.iterator] } for(var item of obj4){ console.log(item); } // 5. 具備非數字和length屬性 // 會執行length所記錄的長度次數,可是沒有對應索引屬性,只會獲得unefined var obj5={ a:'a', length:1, [Symbol.iterator]:Array.prototype[Symbol.iterator] } for(var item of obj5){ console.log(item);// undefined } 複製代碼
var set=new Set().add('a').add('b').add('c') let [x,y]=set; console.log(x,y);//a,b let [a,...b]=set; console.log(a,b);//a,['b','c'] 複製代碼
var str="hello" console.log([...str]);//["h", "e", "l", "l", "o"] var arr=['a',2,6] console.log([...arr]);//["a", 2, 6] 複製代碼
var gen=function *(){ yield 1; yield* [2,3,4]; yield 9 } var res=gen(); console.log(res.next())//{value: 1, done: false} console.log(res.next())//{value: 2, done: false} console.log(res.next())//{value: 3, done: false} console.log(res.next())//{value: 4, done: false} console.log(res.next());//{value: 9, done: false} console.log(res.next());//{value: undefined, done: true} 複製代碼
也有length屬性
,也原生具備iterator接口
var str="hi" //Symbol.iterator屬性是一個函數返回一個遍歷器對象 console.log(typeof str[Symbol.iterator]);//function var res=str[Symbol.iterator](); console.log(res.next());//{value: "h", done: false} console.log(res.next());//{value: "i", done: false} console.log(res.next());//{value: undefined, done: true} 複製代碼
// var str="hello" var str=new String("hello") console.log([...str]);//["h", "e", "l", "l", "o"] str[Symbol.iterator]=function *(){ yield 1; yield 2; yield 3; } console.log([...str]);//[1, 2, 3] 複製代碼
最簡實現:和generator函數結合
// 形式1 var res1={ [Symbol.iterator]:function *(){ yield 1; yield 11; yield 111; } } console.log([...res1]);//[1, 11, 111] // 形式2 var res2={ *[Symbol.iterator](){ yield 2; yield 22; yield 222; } } console.log([...res2]);//[2, 22, 222] 複製代碼
return方法和throw方法
自定義Symbol.iterator方法,那麼return()和throw()可選
for...of循環提早退出的話就會調用return方法
// return 方法 var arr=[2,4,6] arr[Symbol.iterator]=function(){ return { next(){ return {value:'a',done:false} }, return(){ return {done:true} } } } var i=0; for(var item of arr){ i++; console.log(item);//a,a,a,a if(i>3){ // 調用return方法的方式一:break // break; // 2.拋出錯誤 throw new Error("err");//Error: err } } 複製代碼
for...of循環內部調用的就是變量自己的Symbol.iterator方法
而且for...of調用的是遍歷器接口(只會返回具備數字索引的屬性),而for..in不是調用遍歷器接口的,因此能夠獲得非數字索引的屬性
var arr=[1,2,5] arr.foo="ffff" // for...of 循環不能獲得非數字索引的屬性 for(var item of arr){ console.log(item) // 1,2,5 } // for ...in for(var i in arr){ console.log(arr[i]) // 能夠獲得給數字索引的屬性 //1,2,5,ffff } 複製代碼
var arr=['a',2,7] for(var item of arr){ console.log(item)//a,2,7 } var obj={} // 把數組的Symbol.iterator接口設置到對象中 // 注意要使用bind綁定,而不是調用該方法 obj[Symbol.iterator]=arr[Symbol.iterator].bind(arr); for(var item of obj){ console.log(item);//a,2,7 } // 給對象添加新的屬性 obj['a']='yy' obj[3]=3 // 能夠注意到屬性沒有添加到Symbol.iterator中 console.log(obj);//{3: 3, a: "yy", Symbol(Symbol.iterator): ƒ} // 能夠看到BoundThis中沒有屬性a,3 /* arguments: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at Function.invokeGetter (<anonymous>:1:142)] caller: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at Function.invokeGetter (<anonymous>:1:142)] length: 0 name: "bound values" __proto__: ƒ () [[TargetFunction]]: ƒ values() [[BoundThis]]: Array(3) 0: "a" 1: 2 2: 7 length: 3 __proto__: Array(0) [[BoundArgs]]: Array(0) */ for(var item of obj){ // 沒有遍歷到後面添加到的幾個屬性 console.log(item);//a,2,7 } 複製代碼
// 1. set var set=new Set() set.add(1) set.add(10) set.add('a') for(var item of set){ console.log(item);//1,10,a } // 2. Map var map=new Map(); map.set('a',1) map.set('ab',11) map.set('ba',1081) for(var item of map){ console.log(item) // ["a", 1],["ab", 11],["ba", 1081] } 複製代碼
nodelist,arguments,字符串...
// 1.字符串 var str="hello" for(var item of str){ console.log(item);//h,e,l,l,o } // 2. nodelist對象 var nodes=document.getElementsByClassName('one') for(var item of nodes){ console.log(item);//<div class="one"></div> } // 3. arguments對象 function args(){ for(var item of arguments){ console.log(item);//5,'w',98 } } args(5,'w',98) 複製代碼
可是對於對象來講,即便添加了length屬性成爲類數組對象
或者使用Array.from方法轉換爲數組先
var obj={length:2,0:1,1:'e',t:333} // 雖然是類數組對象,可是沒有原生的Symbol.iterator方法,也沒有重寫 // 因此仍是會報錯 /* for(var item of obj){ console.log(item);//TypeError obj is not iterable } */ // 解決方法1,設置Symbol.iterator方法 /* obj[Symbol.iterator]=Array.prototype[Symbol.iterator] for(var item of obj){ console.log(item);//1,e } */ // 解決方法2:使用Array.from轉換爲數組 obj=Array.from(obj) for(var item of obj){ console.log(item);//1,e } 複製代碼
1.經過for..in循環間接獲取鍵值,2.經過Object.keys()生成數組再遍歷
var obj={a:2,0:'ss',1:111} // 1. for...in循環 for(var i in obj){ console.log(obj[i]);//ss,111,2 } // 2. Object.keys()生成數組 for(var item of Object.keys(obj)){ console.log(obj[item])//ss,111,2 } 複製代碼
不可使用break,continue,return實現退出!
var arr=[4,5,77,0,323] arr.forEach((item,i)=>{ console.log(item);//4,5,77,0,323 if(i==2){ // return ;// 退出失敗。不報錯 // break;//報錯,Illegal break statement // continue;// 報錯:Illegal continue statement: no surrounding iteration statement } }) 複製代碼
本文參考 阮一峯ES6教程 node
本文使用 mdnice 排版es6