欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Javascript的迭代器和迭代接口詳解

 更新時間:2022年03月30日 15:22:57   作者:依然范特西fantasy  
這篇文章主要為大家詳細(xì)介紹了Javascript的迭代器和迭代接口,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

1,什么是迭代器

每一個可迭代對象都對應(yīng)著一個可迭代接口[Symbol.iterator];

[Symbol.iterator]接口并不是迭代器,他是一個迭代器工廠函數(shù),調(diào)用該迭代接口即可返回一個待執(zhí)行狀態(tài)的迭代器;

不同的原生全局對象都對應(yīng)著不同的迭代器;

const arr = new Array()
const map = new Map()
const set = new Set()
 
console.log(arr[Symbol.iterator]()) //Array Iterator {}
console.log(map[Symbol.iterator]()) //MapIterator {}
console.log(set[Symbol.iterator]()) //SetIterator {}

將迭代器狀態(tài)從待執(zhí)行狀態(tài)變?yōu)檎嬲膱?zhí)行:調(diào)用迭代器對象的 .next()方法;而其的返回值,就是next()方法的返回值對象:

const iterator = new Array(1, 2, 3, 4)[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: 4, done: false}
console.log(iterator.next()) //{value: undefined, done: true}

可以看到,當(dāng)我執(zhí)行第四次的時候,也就是對應(yīng)著arr[3],但此時返回的對象中,done屬性依舊是false,而執(zhí)行第五次時,value變成了undefined,done變成了true,為什么會出現(xiàn)這種情況呢

在解答這個問題之前,我們需要在重新認(rèn)識一下迭代器:

本質(zhì)上來說,迭代器對象就是實(shí)現(xiàn)了next()方法的對象

const myIterator = {
  next() {
    if (length) {
      return { value: 1, done: false }
    } else {
      return { value: undefined, done: true }
    }
  },
}

如上述,就是一個最簡單的迭代器。調(diào)用next(),會執(zhí)行迭代,返回done為false的迭代器生成對象,直到符合某種條件,返回done為true的迭代器生成對象。

你可以簡單的把Array迭代器原理看作如下所示:

const myIterator = {
  next() {
    if (length) {
      return { value: 1, done: false }
    } else {
      return { value: undefined, done: true }
    }
  },
}

輸出的結(jié)果也是一樣的:

const iterator = new MyArray(1, 2, 3, 4)[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: 4, done: false}
console.log(iterator.next()) //{value: undefined, done: true}

那么,之前的問題就迎刃而解了。

或許你已經(jīng)發(fā)現(xiàn)了,我自定義了一個類MyArray,并且我手動改寫了他的迭代器接口。那么是否只要為某個不可迭代的對象,實(shí)現(xiàn)了[Symbol.iterator],就可以把它變成一個可迭代對象呢?

答案是肯定的。只要你想,你可以為任何對象加上可迭代協(xié)議,并把它變成可迭代對象。因?yàn)榈鷮ο蟮亩x便是:實(shí)現(xiàn)迭代接口的對象。

2,自定義迭代接口

按照以上思路,我們就可以自己手動實(shí)現(xiàn)一個可迭代的Object對象了:

const prototype = {
  [Symbol.iterator]() {
    const entries = Object.entries(this)
    const { length } = entries
    let index = 0
    return {
      next() {
        return index < length
          ? {
              value: {
                key: entries[index][0],
                value: entries[index++][1],
              },
              done: false,
            }
          : { value: undefined, done: true }
      },
    }
  },
}
 
const obj = Object.create(prototype)
obj.name = 'zhang san'
obj.age = 28
 
const objIterator = obj[Symbol.iterator]()
console.log(objIterator.next()) //{value: {key:'name',value:'zhang san'}, done: false}
console.log(objIterator.next()) //{value: {key:'age',value:28}, done: false}
console.log(objIterator.next()) //{value: undefined, done: true}

首先,我們聲明了一個改寫了迭代接口的對象,接著用Obejct.create()創(chuàng)建了以此對象為原型的obj。

該對象實(shí)例本身是沒有迭代接口的,但是會沿著原型鏈去尋找prototype對象是否存在迭代接口。只要能在其原型鏈上找到迭代接口,那么就代表其是一個可迭代對象。如:

const objSon = Object.create(obj, {
  name: {
    enumerable: true,
    writable: true,
    value: 'zhang xiao san',
  },
  age: {
    enumerable: true,
    writable: false,
    value: 2,
  },
  secret: {
    enumerable: false,
    writable: true,
    value: 'secret',
  },
})
const sonIterator = objSon[Symbol.iterator]()
 
console.log(sonIterator.next()) //{value: {key:'name',value:'zhang xiao san'}, done: false}
console.log(sonIterator.next()) //{value: {key:'age',value:2}, done: false}
console.log(sonIterator.next()) //{value: undefined, done: true}

objSon依舊是一個可迭代對象。

那么現(xiàn)在,我們就通過改造迭代接口[Symbol.iterator]的方式,把一個原本不是迭代類型的對象,變成了可迭代對象。

我們可以用該迭代接口來遍歷任何enumerable的屬性。但如果你想將enumrable為false的secret屬性也遍歷出來,那么只需要將迭代接口中的entries改造一下即可,一切皆由你想:

// const entries = Object.entries(this)
 
    const ownKeys = Object.getOwnPropertyNames(this)
    const entries = ownKeys.reduce(
      (result, key) => [...result, [key, this[key]]],
      []
    )

3,原生語言的迭代

以for - of 為例

for (const item of obj) {
  console.log(item)
}
//{key:'name',value:'zhang san'}
//{key:'age',value:28}
 
for (const item of objSon) {
  console.log(item)
}
//{key:'name',value:'zhang xiao san'}
//{key:'age',value:2}

可以看到,無論是obj,還是objSon,都可以正常用for of 循環(huán),并且返回值為迭代器生成對象value屬性的值。

你可以這么理解for - of 的機(jī)制:后臺調(diào)用提供的可迭代對象的工廠函數(shù)[Symbol.iterator],從而創(chuàng)建一個迭代器,然后自動調(diào)用迭代器next執(zhí)行。done為false,則將迭代器生成對象value賦值給item;done為true,則跳出循環(huán):

const objIterator = obj[Symbol.iterator]()
{
  const { value: item,done } = objIterator.next()
  if(done) break
  console.log(item) //{key:'name',value:'zhang san'}
}
{
  const { value: item,done } = objIterator.next()
  if(done) break
  console.log(item) //{key:'age',value:28}
}
{
  const { value: item,done } = objIterator.next()
  if(done) break
  console.log(item) 
}

不僅僅是for - of,原生語言的迭代機(jī)制,都與此類似。數(shù)組解構(gòu),拓展操作符,Array.from,new Set(),new Map(),Promise.all(),Promise.race(),yield * 操作符等,都屬于原生迭代語言。

了解了for - of 循環(huán)的機(jī)制之后,大家可以觀察一下下面的例子:

const arr = [1, 2, 3, 4, 5, 6, 7, 8]
for (const item of arr) {
  console.log(item)
  if (item > 3) break
}
for (const item of arr) {
  console.log(item)
}
//輸出結(jié)果:1,2,3,4 | 1,2,3,4,5,6,7,8
 
const arrIterator = arr[Symbol.iterator]()
for (const item of arrIterator) {
  console.log(item)
  if (item > 3) break
}
for (const item of arrIterator) {
  console.log(item)
}
//輸出結(jié)果:1,2,3,4 | 5,6,7,8

循環(huán)arr的輸出結(jié)果與循環(huán)arr迭代器arrIterator的結(jié)果明顯的不同。為什么會出現(xiàn)這種現(xiàn)象呢?

在迭代器對象中,還有一個很重要的知識點(diǎn):迭代器對象是一個一次性的,不可逆的對象。

因此,在迭代中某個地方終止,那么只能接著上一次終止的位置繼續(xù)執(zhí)行,而不會從頭開始。

那么為什么對于arr本身使用for - of,卻沒有接著執(zhí)行而是從頭開始呢?可以回到介紹for - of 循環(huán)機(jī)制的那部分,其中有一句話:后臺調(diào)用提供的可迭代對象的工廠函數(shù)[Symbol.iterator],從而創(chuàng)建一個迭代器。

也就是說,每調(diào)用一次for - of循環(huán),都會創(chuàng)建一個新的迭代器對象,而該迭代器對象,在循環(huán)結(jié)束時就會被當(dāng)作垃圾對象被回收。

雖然arr連續(xù)調(diào)用了兩次for - of循環(huán),但是在循環(huán)體的內(nèi)部,并不是同一個迭代器對象。因此,即使上一個迭代器在item>3這個條件處中止了,但是下一次循環(huán)的迭代器對象,是一個全新的,還沒有執(zhí)行過的迭代器對象。

那么對于調(diào)用arr[Symbol.iterator]接口生成的的迭代器對象arrIterator,為什么會出現(xiàn)繼續(xù)上次執(zhí)行的情況呢,換句話說,為什么arrIterator的兩次for循環(huán),沒有產(chǎn)生兩次迭代器對象?

其實(shí),迭代器對象本身,也實(shí)現(xiàn)了迭代器接口,也就是說。arr有一個迭代器接口[Symbol.iterator],而迭代器對象arrIterator也有一個迭代器接口[Symbol.iterator],并且,調(diào)用該迭代器對象,返回其本身:

console.log(arrIterator[Symbol.iterator]() === arrIterator) // true

所以雖然每次執(zhí)行for-of循環(huán)都會同樣的調(diào)用迭代器接口,但是該迭代器接口返回的對象就是arr的迭代器對象本身,而迭代器對象是一個一次性,不可逆的對象。因此,為什么會出現(xiàn)上述現(xiàn)象,也就顯而易見了。

細(xì)心的朋友可能會想到一個問題:我們在之前手動實(shí)現(xiàn)的可迭代對象,其迭代器對象是否支持了迭代接口?是的,他不能。

for (const item of objIterator) {
  console.log(item)
}
//Uncaught TypeError: objIterator is not iterable

但其實(shí)我們要優(yōu)化也很簡單,只要將該迭代器對象也實(shí)現(xiàn)一個迭代接口,并且該迭代接口工廠函數(shù)返回其本身,即可。

const prototype = {
  [Symbol.iterator]() {
    // const entries = Object.entries(this)
 
    const ownKeys = Object.getOwnPropertyNames(this)
    const entries = ownKeys.reduce(
      (result, key) => [...result, [key, this[key]]],
      []
    )
    const { length } = entries
    let index = 0
    return {
      next() {
        return index < length
          ? {
              value: {
                key: entries[index][0],
                value: entries[index++][1],
              },
              done: false,
            }
          : { value: undefined, done: true }
      },
      [Symbol.iterator]() {
        return this
      },
    }
  },
}
 
const objIterator = obj[Symbol.iterator]()
 
for (const item of objIterator) {
  console.log(item)
  if (item.key === 'name') break
}
for (const item of objIterator) {
  console.log(item)
}
// {key: 'name', value: 'zhang san'}
// {key: 'age', value: 28}

現(xiàn)在,你可以用文中所介紹的方法,將任何對象變成一個可迭代對象。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

最新評論