Iterator:访问数据集合的统一接口
導(dǎo)語
遍歷器Iterator是ES6為訪問數(shù)據(jù)集合提供的統(tǒng)一接口。任何內(nèi)部部署了遍歷器接口的數(shù)據(jù)集合,對于用戶來說,都可以使用相同方式獲取到相應(yīng)的數(shù)據(jù)結(jié)構(gòu)。如果使用的是最新版Chrome瀏覽器,那么你要知道——我們所熟悉的數(shù)組小姐,已悄悄的打開了另一扇可抵達(dá)她心扉的小徑。
1 正題
某個(gè)數(shù)據(jù)集合部署了Iterator接口,是指其Symbol.iterator屬性指向一個(gè)能返回Iterator接口的函數(shù)。任何默認(rèn)使用遍歷器訪問數(shù)據(jù)集合的方法,都會調(diào)用此屬性以得到遍歷器對象,再按照設(shè)定的順序依次訪問該數(shù)據(jù)結(jié)構(gòu)的成員(關(guān)于Symbol.iterator請看最后一節(jié)的延伸閱讀)。比如原生數(shù)組的遍歷器為[][Symbol.iterator],也可以直接通過其構(gòu)造函數(shù)的原型獲取Array.prototype[Symbol.iterator]。
1.1 基本行為
調(diào)用Iterator接口會返回一個(gè)新的遍歷器對象(指針對象)。
對象中必然有next方法,用于訪問下一個(gè)數(shù)據(jù)成員。指針初始時(shí)指向當(dāng)前數(shù)據(jù)結(jié)構(gòu)的起始位置。
第一次調(diào)用對象的next方法,指針指向數(shù)據(jù)結(jié)構(gòu)的第一個(gè)成員。
第二次調(diào)用對象的next方法,指針指向數(shù)據(jù)結(jié)構(gòu)的第二個(gè)成員。
不斷的調(diào)用對象的next方法,直到它指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置。
每次調(diào)用next方法,都會返回相同的數(shù)據(jù)結(jié)構(gòu):{ value, done }。
其中value表示當(dāng)前指向成員的值,沒有則為undefined。
其中done是一個(gè)布爾值,表示遍歷是否結(jié)束,結(jié)束為true,否則false。
遍歷器接口的標(biāo)準(zhǔn)十分簡潔,不提供諸如:操作內(nèi)部指針、判斷是否有值等等方法。只需要一直不斷的調(diào)用next方法,當(dāng)done為false時(shí)獲取當(dāng)時(shí)的value,done為true時(shí)停止即可。第一次接觸遍歷器的行為模式是在2016的冬天,那時(shí)底蘊(yùn)不夠雞毛也沒長全,理解不了簡潔性的適用和強(qiáng)大。直到現(xiàn)在——在即將打包被迫離開公司的前夕才驀然的醒覺。多么痛的領(lǐng)悟啊。
let iterator = [1, 2, 3][Symbol.iterator]();console.log( iterator.next() ); // {value: 1, done: false} console.log( iterator.next() ); // {value: 2, done: false} console.log( iterator.next() ); // {value: 3, done: false} console.log( iterator.next() ); // {value: undefined, done: true}1.2 簡單實(shí)現(xiàn)
面向不同的數(shù)據(jù)結(jié)構(gòu),有不同的遍歷器實(shí)現(xiàn)方法,我們簡單的實(shí)現(xiàn)下數(shù)組的遍歷器方法。
let res = null; let iterator = myIterator([3, 7]);console.log( iterator.next() ); // {value: 3, done: false} console.log( iterator.next() ); // {value: 7, done: false} console.log( iterator.next() ); // {value: undefined, done: true}function myIterator(array = []) {let index = 0;return {next() {return index < array.length ? { value: array[index++], done: false }: { value: undefined, done: true };}}; }1.3 return & throw
除了為遍歷器對象部署next方法,還可以有return和throw方法。其中return方法會在提前退出for of循環(huán)時(shí)(通常是因?yàn)槌鲥e(cuò),或觸發(fā)了break語句)被調(diào)用。而throw方法主要是配合Generator函數(shù)使用,一般的遍歷器對象用不到這個(gè)方法,所以不予介紹。
let obj = {[Symbol.iterator]() {let index = 0;let array = [1, 2, 3];return {next() {return index < array.length ? { value: array[index++], done: false }: { value: undefined, done: true };},return() {console.log('Trigger return.');return {};}};} };for (let v of obj) {console.log(v); // 打印出:1, 2, 3,沒觸發(fā) return 函數(shù)。 }for (let v of obj) {if (v === 2) break;console.log(v); // 打印出:1,之后觸發(fā) return 函數(shù)。 }for (let v of obj) {if (v === 3) break;console.log(v); // 打印出:1, 2,之后觸發(fā) return 函數(shù)。 }for (let v of obj) {if (v === 4) break;console.log(v); // 打印出:1, 2, 3,沒觸發(fā) return 函數(shù)。 }for (let v of obj) {if (v === 2) throw Error('error');console.log(v); // 打印出:1,之后觸發(fā) return 函數(shù),并報(bào)錯(cuò)停止執(zhí)行。 }2 原生支持
2.1 默認(rèn)持有遍歷器
原生默認(rèn)持有遍歷器接口的數(shù)據(jù)結(jié)構(gòu)有:
基本類型:Array, Set, Map(四種基本數(shù)據(jù)集合:Array, Object, Set 和 Map)。
類數(shù)組對象:arguments, NodeList, String。
遍歷器與先前的遍歷方法
一個(gè)數(shù)據(jù)集合擁有遍歷器接口,并不意味著所有遍歷它的方法都是使用此接口。實(shí)際上,只有ES6新增的幾種方式和某些方法會使用,下面會有介紹。以數(shù)組來說,對其使用for和for of雖然可訪問到相同的成員,但是實(shí)際的操作方式卻不同。
對象沒有默認(rèn)的遍歷器接口
為什么對象沒有默認(rèn)的遍歷器接口?這要從兩方面說明。一為遍歷器是種線性處理結(jié)構(gòu),對于任何非線性的數(shù)據(jù)結(jié)構(gòu),部署了遍歷器接口,就等于部署一種線性轉(zhuǎn)換。二是對象本來就是一個(gè)無序的集合,如果希望其有序,可以使用Map代替。這即是各有其長,各安其職。屎殼郎如果不滾糞球而去采蜜,那,呃,花妹妹可能就遭殃咯。
自行生成的類數(shù)組對象(擁有l(wèi)ength屬性),不具備遍歷器接口。這與String等原生類數(shù)組對象不同,畢竟人家是親生的,一出生就含著金鑰匙(也不怕誤吞)。不過我們可以將數(shù)組的遍歷器接口直接應(yīng)用于自行生成的類數(shù)組對象,簡單有效無副作用。
let obj = {0: 'a',1: 'b',length: 2,[Symbol.iterator]: Array.prototype[Symbol.iterator] };let iterator = obj[Symbol.iterator](); console.log( iterator.next() ); // {value: "a", done: false} console.log( iterator.next() ); // {value: "b", done: false} console.log( iterator.next() ); // {value: undefined, done: true}為對象添加遍歷器接口,也不影響之前不使用遍歷器的方法,比如for in, Object.keys等等(兩者不等同)。
let obj = {0: 'a',1: 'b',length: 2,[Symbol.iterator]: Array.prototype[Symbol.iterator] };console.log( Object.keys(obj) ); // ["0", "1", "length"]for (let v of obj) {console.log(v); // 依次打印出:"a", "b"。 }for (let k in obj) {console.log(k); // 依次打印出:"0", "1", "length"。 }2.2 默認(rèn)調(diào)用遍歷器
for of
for of是專門用來消費(fèi)遍歷器的,其遍歷的是鍵值(for in遍歷的是鍵名)。
擴(kuò)展運(yùn)算符
無論是解構(gòu)賦值或擴(kuò)展運(yùn)算都是默認(rèn)調(diào)用遍歷器的。
yield*
在Generator函數(shù)中有yield*命令,如果其后面跟的是一個(gè)可遍歷的結(jié)構(gòu),它會調(diào)用該結(jié)構(gòu)的遍歷器接口。
其它場合
有些接受數(shù)組作為參數(shù)的函數(shù),會默認(rèn)使用數(shù)組的遍歷器接口,所以也等同于默認(rèn)調(diào)用。比如Array.from(), Promise.all()。
延伸閱讀
關(guān)于ES6的Symbol:鏈接。
總結(jié)
以上是生活随笔為你收集整理的Iterator:访问数据集合的统一接口的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Call to undefined fu
- 下一篇: Mybatis 关键组件(注意各组件的最