国产99久久精品_欧美日本韩国一区二区_激情小说综合网_欧美一级二级视频_午夜av电影_日本久久精品视频

最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
當前位置: 首頁 - 科技 - 知識百科 - 正文

JavaScript迭代器的含義及用法

來源:懂視網 責編:小采 時間:2020-11-27 21:54:46
文檔

JavaScript迭代器的含義及用法

JavaScript迭代器的含義及用法:什么是迭代器 迭代器就是為實現對不同集合進行統一遍歷操作的一種機制,只要給需要遍歷的數據結構部署Iterator接口,通過調用該接口,或者使用消耗該接口的API實現遍歷操作。 迭代器模式 在接觸迭代器之前,一起先了解什么是迭代器模式,回想一下我們生活中
推薦度:
導讀JavaScript迭代器的含義及用法:什么是迭代器 迭代器就是為實現對不同集合進行統一遍歷操作的一種機制,只要給需要遍歷的數據結構部署Iterator接口,通過調用該接口,或者使用消耗該接口的API實現遍歷操作。 迭代器模式 在接觸迭代器之前,一起先了解什么是迭代器模式,回想一下我們生活中

什么是迭代器

迭代器就是為實現對不同集合進行統一遍歷操作的一種機制,只要給需要遍歷的數據結構部署Iterator接口,通過調用該接口,或者使用消耗該接口的API實現遍歷操作。

迭代器模式

在接觸迭代器之前,一起先了解什么是迭代器模式,回想一下我們生活中的事例。我們在參觀景區需要買門票的時候,售票員需要做的事情,他會對排隊購票的每一個人依次進行售票,對普通成人,對學生,對兒童都依次售票。售票員需要按照一定的規則,一定順序把參觀人員一個不落的售完票,其實這個過程就是遍歷,對應的就是計算機設計模式中的迭代器模式。迭代器模式,提供一種方法順序訪問一個聚合對象中的各種元素,而又不暴露該對象的內部表示。

為什么要有迭代器

回憶在我們的javascript中,可遍歷的結構以及方式有很多。JavaScript 原有的表示“集合”的數據結構,主要是數組(Array)和對象(Object),ES6 又添加了Map和Set,這樣就有了四種數據集合,而遍歷這四種結構都有不同的方法。舉個栗子,服務端提供數據給前端,前端進行數據可視化工作,對數據進行遍歷展示使用的for,但是由于業務的變化,使得后端返回的數據結構發生變化,返回對象或者是set,map,導致前端遍歷代碼大量重寫。而迭代器的目的就是要標準化迭代操作。

如何部署迭代器接口

ES6為迭代器引入了一個隱式的標準化接口。Javascript許多內建的數據結構,例如Array、Map、Set、String、TypedArray、函數的 arguments 對象、NodeList 對象都具備 Iterator 接口。可以通過在控制臺打印一個Array實例,查看其原型上具有一個Symbol.iterator屬性(Symbol.iterator其實是Symbol('Symbol.iterator')的簡寫,屬性名是Symbol類型代表著這個屬性的唯一以及不可重寫覆蓋),它就是迭代器函數,執行這個函數,就會返回一個迭代器對象。

雖然Javascript許多內建的數據結構已經實現了該接口,還有些結構是沒有迭代器接口的(比如對象),那怎么辦,我們需要寫迭代器,那么就需要知道迭代器是如何工作的。下面代碼實現的一個簡單迭代器:

//迭代器就是一個函數,也叫迭代器生成函數
function Iterator(o){
let curIndex = 0;
let next = () => {
return {
value: o[curIndex],
done: o.length == ++curIndex
}
}
//返回迭代對象,該對象有next方法
return {
next
}
}
let arr = [1,2]
let oIt = Iterator(arr)
oIt.next();//{value:1,done:false}
oIt.next();//{value:2,done:false}
oIt.next();// {value: undefined, done: true}
oIt.next();// {value: undefined, done: true}

調用迭代器函數,返回一個對象,該對象就是迭代器對象,對象上擁有next方法,每一次調用next方法,都會返回數據結構的當前成員的信息。具體來說,就是返回一個包含value和done兩個屬性的對象。其中,value屬性是當前成員的值,done屬性是一個布爾值,表示遍歷是否結束。
next()迭代

在上面調用next方法的栗子中,需要注意的是:

在獲得數組最后一位元素的時候,迭代器不會報告done:true,這時候需要再次調用next(),越過數組結尾的值,才能得到完成信號done:true。

通常情況下,在已經迭代完畢的迭代器對象上繼續調用next方法會繼續返回{value: undefined, done: true}而不會報錯。

可選的return()和throw()

遍歷器對象除了必須具有next方法,還可以具有可選的return方法和throw方法。

return方法被定義為向迭代器發送一個信號,表明不會在消費者中再提取出任何值。

Object.prototype[Symbol.iterator] = function () {
let curIndex = 0;
let next = () => {
return {
value: this[curIndex],
done: this.length == curIndex++
}
}
return {
next,
return() {
console.log('執行return啦')
return {}
}
}
}
let obj = {
0: 'a',
1: 'b',
2: 'c'
}
//自動調用---遇到對迭代器消耗提前終止的條件
for (let item of obj) {
if (item == 'c') {
break
} else {
console.log(item)
}
}
//自動調用---拋出異常
for (let item of obj) {
if (item == 'c') {
throw new Error('Errow')
} else {
console.log(item)
}
}
//手動調用
let ot = obj[Symbol.iterator]()
console.log(ot.return())

上面代碼中,throw方法的執行可以在某種情況下自動被調用,也可以手動調用。throw方法主要向迭代器報告一個異常/錯誤,一般配合生成器使用。

迭代器分類

迭代器分為內部迭代器和外部迭代器。

  • 內部迭代器:本身是函數,該函數內部定義好迭代規則,完全接受整個迭代過程,外部只需要一次調用。例如Array.prototype.forEach方法、jQuery.each都是內部迭代器。
  • 外部迭代器:本身是函數,執行返回迭代對象,迭代下一個元素必須顯式調用。使用forEach遍歷,只可以一次性把數據全部拉取消耗,而迭代器可以用于以一次一步的方式控制行為,使得迭代過程更加靈活可控。
  • 迭代器使用

    實現迭代器接口后,如何進行使用?

    let arr = ['a', 'b'];
    let iter = arr[Symbol.iterator]();
    iter.next() // { value: 'a', done: false }
    iter.next() // { value: 'b', done: false }
    iter.next() // { value: undefined, done: true }

    除了像上述代碼這樣單獨使用外,實現該接口的目的,就是為所有數據結構,提供一種統一的訪問機制。實現了該接口,就可以調用ES6中新增的通過調用Iterator 接口實現的API,例如for..of就是典型的消耗迭代器的API。下面具體看看for..of的實現原理:

    let arr = [1,2,3];
    for(let num of arr){
    console.log(num);
    }

    輸出結果為:1,2,3

    for-of 循環首先會調用 arr 數組中Symbol.iterator 屬性對象的函數,就會獲取到該數組對應的迭代器,接下來 iterator.next()被調用,迭代器結果對象的 value 屬性會被放入到變量 num 中。數組中的數據項會依次存入到變量num 中,直到迭代器結果對象中的 done 屬性變成 true 為止,循環就結束。

    for-of 循環完全刪除了for循環中追蹤集合索引的需要,更能專注于操作集合內容。

    ES6 規定,默認的 Iterator 接口部署在數據結構的Symbol.iterator屬性,或者說,一個數據結構只要具有Symbol.iterator屬性,就可以認為是“可遍歷的”(iterable)。就可以使用上述默認會調用Iterator函數的API,而如果該數據結構沒有提供實現這個接口(例如對象)又該怎么樣達到最大化的互操作性呢?那么就可以自己構建符合這個標準的迭代器。

    下面是一個為對象添加 Iterator 接口的例子:

    let obj = {
    0: 'a',
    1: 'b',
    2: 'c',
    length: 3,
    [Symbol.iterator]: function () {
    let curIndex = 0;
    let next = () => {
    return {
    value: this[curIndex],
    done: this.length == curIndex++
    }
    }
    return {
    next
    }
    }
    }
    for (let item of obj) {
    console.log(item)
    }

    如果把該對象的[Symbol.iterator]屬性刪除,那么就會報錯Uncaught TypeError: obj is not iterable,告訴我們obj是不可被遍歷。

    除了上面展示的for..of循環可以一個一個的消耗迭代器之外,還有其它ES6結構也可以用來消耗迭代器。例如spread運算符:

    function f(x, y, z) {
    console.log(x, y, z)
    }
    f(...[2, 3, 1])

    以及結構賦值也可以部分或者完全消耗一個迭代器:

    let arr = [1, 2, 3, 4, 5]
    var it = arr[Symbol.iterator]()
    //部分消耗
    var [x, y] = it
    console.log(x, y) //打印1 2
    //完全消耗
    var [y, ...z] = it
    console.log(y, z) //打印3 [4,5]

    JavaScript 默認產生迭代器的API

    產生迭代器對象,我們可以通過定義迭代器函數來生產迭代器對象,還可以調用JavaScript在內置數據結構中定義好的迭代器函數來生產。除此之外,對于數組以及ES6新增的幾個新的數據結構MAP、Set,這些集合不僅本身已部署迭代器接口,還提供了API方法來產生迭代器對象。ES6 的數組、Set、Map 都部署了以下三個方法,調用后都返回遍歷器對象。

  • entries() 返回一個遍歷器對象,用來遍歷[鍵名, 鍵值]組成的數組。
  • keys() 返回一個遍歷器對象,用來遍歷所有的鍵名。
  • values() 返回一個遍歷器對象,用來遍歷所有的鍵值。
  • 數組的迭代器使用實例

    下面是數組的迭代器接口使用:

    let arr = [1,2,3,4]
    let arrEntires = arr.entries()
    arrEntires.next() //{value: [0, 1], done: false}
    let arrKeys = arr.keys() //對于數組,索引值就是鍵值
    arrKeys.next() //{value: 0, done: false}
    let arrValues = arr.values()
    arrValues.next() //{value: 1, done: false}

    下面代碼可以看出數組的for…of 遍歷的默認迭代器接口是values

    for(let item of [1,2,3]) {
    console.log(item)// [1,2,3]
    }

    Set的迭代器使用實例

    下面是Set的迭代器接口使用:

    let set = new Set([1,2,3,4])
    let setEntires = set.entries()//對于 Set,鍵名與鍵值相同。
    setEntires.next() //{value: [1, 1], done: false}
    let setKeys = set.keys()
    setKeys.next() //{value: 1, done: false}
    let setValues = set.values()
    setValues.next() //{value: 1, done: false}

    如下可以看出Set的默認迭代器接口[Symblo.iterator]是values

    for(let item of new Set([1,2,3,4])){
    console.log(item)// [1,2,3,4]
    }

    Map的迭代器使用實例

    下面是Map的迭代器接口使用:

    let map = new Map([[1,2],[3,4]])
    let mapEntires = map.entries()
    mapEntires.next() //{value: [1, 2], done: false}
    let mapKeys = map.keys() 
    mapKeys.next() //{value: 1, done: false}
    let mapValues = map.values()
    mapValues.next() //{value: 2, done: false}

    Map 的默認迭代器接口[Symblo.iterator]是 entries;

    for(let item of new Map([[1,2],[3,4]])){
    console.log(item)// [1,2] [3,4]
    }

    為什么對象沒有內置迭代器接口

    在上面中,我們提及到對象沒有設置可迭代的默認方法,是不可迭代對象,表現為其沒有[Symbol.iterator]屬性。雖然對象對我們來說,是鍵值存儲的一種方式,盡管沒有 map 那么好,key只可以是字符串,但是有的時候對象也是需要被迭代的,但是為什么不給對象設置可迭代的默認方法?

    原因是因為,對于對象的遍歷,需要考慮到遍歷是對象自身的屬性還是遍歷對象自身上的可枚舉屬性還是遍歷原型上的屬性還是遍歷原型上的可枚舉屬性還是連[Symbol.iterator]也希望遍歷出來。鑒于各方意見不一,并且現有的遍歷方式可以滿足,于是標準組沒有將[Symbol.iterator]加入。

    生成迭代器對象的方法

    在上面,我們嘗試過了為一個對象添加了Symbol.iterator方法,該方法就是該對象的遍歷器生成函數,調用該函數會返回該對象的一個遍歷器對象。

    除了上面在為對象添加遍歷器生成函數的這種根據迭代器協議直接生成迭代器對象的方式外,還有什么方式可以生成迭代器對象呢?有,它是一種特殊的函數,叫生成器。

    var it = {};
    it[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
    };
    //可以被...遍歷,說明已經部署成功
    console.log([...it])// [1, 2, 3]
    let myIterator = it[Symbol.iterator]()
    console.log(myIterator.next())//{value: 1, done: false}
    console.log(myIterator.next())//{value: 2, done: false}
    console.log(myIterator.next())//{ value: 3, done: false }
    console.log(myIterator.next())//{ value: undefined, done: true }

    上面代碼中,生成器函數沒有過多的代碼,只需要使用關鍵字yeild來返回每次next()的值。

    生成器是一種特殊的函數形式,生成器函數的聲明語法為:

    function *bar(){
    // ...
    }

    *前后可以有空格也可以沒有空格。生成器函數的聲明雖然和普通函數有區別,但是執行和普通函數一樣,一樣可以傳參數。那它們的主要區別是什么呢?

    函數是一段執行特定任務的代碼塊,所以函數執行,相當于這一段代碼塊被執行。函數開始執行,在它執行完之前不會被打斷,這段代碼塊將被全部執行完。在ES6引入生成器之前函數的確是這樣執行的,但是前面介紹到外部迭代器可以相比內部迭代器對迭代過程進行控制,什么時候需要消耗,迭代器對象再next一下即可。類似迭代過程,函數的執行過程一樣可以控制,函數可以不需要一次性執行完畢。

    生成器函數的執行會返回一個迭代器對象來控制該生成器函數執行其代碼。因此,函數的執行變得可控。還可以在生成器中使用新的關鍵字yield,用來標示一個暫停點。迭代器除了可以控制函數執行外,還可以在每一次暫停中雙向傳遞信息,暫停的時候生成器函數會返回一個值,恢復執行的時候迭代器可以通過向next方法傳參向函數內部傳遞一個值。可以理解為多次傳參,多個返回值。

    聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    JavaScript迭代器的含義及用法

    JavaScript迭代器的含義及用法:什么是迭代器 迭代器就是為實現對不同集合進行統一遍歷操作的一種機制,只要給需要遍歷的數據結構部署Iterator接口,通過調用該接口,或者使用消耗該接口的API實現遍歷操作。 迭代器模式 在接觸迭代器之前,一起先了解什么是迭代器模式,回想一下我們生活中
    推薦度:
    標簽: 使用 js javascript
    • 熱門焦點

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 中文字幕日韩一区二区三区不卡 | 免费一级特黄a | 欧美第四页| 亚洲欧美日韩国产综合高清 | 情侣国产在线 | 国产一区二区三区夜色 | 欧美一区二区日韩一区二区 | 久艹网 | 美女一丝不佳一级毛片大屁股 | 欧美.亚洲.日本一区二区三区 | 亚洲精品在线免费观看 | 国产精品第5页 | 国产一区二区三区视频在线观看 | 亚洲免费久久 | 爽妇网s | 日本一道在线 | 亚洲国产综合专区在线电影 | 成人久久精品一区二区三区 | 国产精品久久久久aaaa | 香港一级a毛片在线播放 | 最新偷窥盗摄视频在线 | 日韩欧美在线观看视频 | 日韩欧美专区 | 日韩在线国产 | 久久精品免费观看 | 久久精品成人国产午夜 | 亚洲欧洲一区二区 | 日韩在线观看精品 | 久久伊人色 | 欧美a色爱欧美综合v | 99久久精品国内 | 亚洲综合一区二区精品久久 | 国产精品手机视频一区二区 | 国产一级淫片免费播放 | 国产一区三区二区中文在线 | 欧美性猛交一区二区三区精品 | 欧美精品小视频 | 不卡一级aaa全黄毛片 | 久久久久女人精品毛片九一 | 日韩欧美在线不卡 | 在线免费观看国产视频 |