最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JS Generator函數(shù)yield表達(dá)式示例詳解

 更新時(shí)間:2022年10月29日 15:20:45   作者:喵了個(gè)咪同學(xué)  
這篇文章主要為大家介紹了JS Generator函數(shù)yield表達(dá)式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

什么是 Generator 函數(shù)

在Javascript中,一個(gè)函數(shù)一旦開(kāi)始執(zhí)行,就會(huì)運(yùn)行到最后或遇到return時(shí)結(jié)束,運(yùn)行期間不會(huì)有其它代碼能夠打斷它,也不能從外部再傳入值到函數(shù)體內(nèi)

而Generator函數(shù)(生成器)的出現(xiàn)使得打破函數(shù)的完整運(yùn)行成為了可能,其語(yǔ)法行為與傳統(tǒng)函數(shù)完全不同

Generator函數(shù)是ES6提供的一種異步編程解決方案,形式上也是一個(gè)普通函數(shù),但有幾個(gè)顯著的特征:

  • function關(guān)鍵字與函數(shù)名之間有一個(gè)星號(hào) "*" (推薦緊挨著function關(guān)鍵字)
  • 函數(shù)體內(nèi)使用 yield 表達(dá)式,定義不同的內(nèi)部狀態(tài) (可以有多個(gè)yield)
  • 直接調(diào)用 Generator函數(shù)并不會(huì)執(zhí)行,也不會(huì)返回運(yùn)行結(jié)果,而是返回一個(gè)遍歷器對(duì)象(Iterator Object)
  • 依次調(diào)用遍歷器對(duì)象的next方法,遍歷 Generator函數(shù)內(nèi)部的每一個(gè)狀態(tài)
  // 傳統(tǒng)函數(shù)
  function foo() {
    return 'hello world'
  }
  foo()   // 'hello world',一旦調(diào)用立即執(zhí)行
  // Generator函數(shù)
  function* generator() {
    yield 'status one'         // yield 表達(dá)式是暫停執(zhí)行的標(biāo)記  
    return 'hello world'
  }
  let iterator = generator()   // 調(diào)用 Generator函數(shù),函數(shù)并沒(méi)有執(zhí)行,返回的是一個(gè)Iterator對(duì)象
  iterator.next()              // {value: "status one", done: false},value 表示返回值,done 表示遍歷還沒(méi)有結(jié)束
  iterator.next()              // {value: "hello world", done: true},value 表示返回值,done 表示遍歷結(jié)束

上面的代碼中可以看到傳統(tǒng)函數(shù)和Generator函數(shù)的運(yùn)行是完全不同的,傳統(tǒng)函數(shù)調(diào)用后立即執(zhí)行并輸出了返回值;Generator函數(shù)則沒(méi)有執(zhí)行而是返回一個(gè)Iterator對(duì)象,并通過(guò)調(diào)用Iterator對(duì)象的next方法來(lái)遍歷,函數(shù)體內(nèi)的執(zhí)行看起來(lái)更像是“被人踢一腳才動(dòng)一下”的感覺(jué)

  function* gen() {
    yield 'hello'
    yield 'world'
    return 'ending'
  }
  let it = gen()
  it.next()   // {value: "hello", done: false}
  it.next()   // {value: "world", done: false}
  it.next()   // {value: "ending", done: true}
  it.next()   // {value: undefined, done: true}

上面代碼中定義了一個(gè) Generator函數(shù),其中包含兩個(gè) yield 表達(dá)式和一個(gè) return 語(yǔ)句(即產(chǎn)生了三個(gè)狀態(tài))

每次調(diào)用Iterator對(duì)象的next方法時(shí),內(nèi)部的指針就會(huì)從函數(shù)的頭部或上一次停下來(lái)的地方開(kāi)始執(zhí)行,直到遇到下一個(gè) yield 表達(dá)式或return語(yǔ)句暫停。換句話說(shuō),Generator 函數(shù)是分段執(zhí)行的,yield表達(dá)式是暫停執(zhí)行的標(biāo)記,而 next方法可以恢復(fù)執(zhí)行

第四次調(diào)用next方法時(shí),由于函數(shù)已經(jīng)遍歷運(yùn)行完畢,不再有其它狀態(tài),因此返回 {value: undefined, done: true}。如果繼續(xù)調(diào)用next方法,返回的也都是這個(gè)值

yield 表達(dá)式

yield 表達(dá)式只能用在 Generator 函數(shù)里面,用在其它地方都會(huì)報(bào)錯(cuò)

function (){
    yield 1;
  })()
  // SyntaxError: Unexpected number
  // 在一個(gè)普通函數(shù)中使用yield表達(dá)式,結(jié)果產(chǎn)生一個(gè)句法錯(cuò)誤

yield 表達(dá)式如果用在另一個(gè)表達(dá)式中,必須放在圓括號(hào)里面

function* demo() {
    console.log('Hello' + yield); // SyntaxError
    console.log('Hello' + yield 123); // SyntaxError
    console.log('Hello' + (yield)); // OK
    console.log('Hello' + (yield 123)); // OK
  }

yield 表達(dá)式用作參數(shù)或放在賦值表達(dá)式的右邊,可以不加括號(hào)

function* demo() {
    foo(yield 'a', yield 'b'); // OK
    let input = yield; // OK
  }

yield 表達(dá)式和return語(yǔ)句的區(qū)別

相似:都能返回緊跟在語(yǔ)句后面的那個(gè)表達(dá)式的值

區(qū)別:

  • 每次遇到 yield,函數(shù)就暫停執(zhí)行,下一次再?gòu)脑撐恢美^續(xù)向后執(zhí)行;而 return 語(yǔ)句不具備記憶位置的功能
  • 一個(gè)函數(shù)只能執(zhí)行一次 return 語(yǔ)句,而在 Generator 函數(shù)中可以有任意多個(gè) yield

yield* 表達(dá)式

如果在 Generator 函數(shù)里面調(diào)用另一個(gè) Generator 函數(shù),默認(rèn)情況下是沒(méi)有效果的

function* foo() {
    yield 'aaa'
    yield 'bbb'
  }
  function* bar() {
    foo()
    yield 'ccc'
    yield 'ddd'
  }
  let iterator = bar()
  for(let value of iterator) {
    console.log(value)
  }
  // ccc
  // ddd

上例中,使用 for...of 來(lái)遍歷函數(shù)bar的生成的遍歷器對(duì)象時(shí),只返回了bar自身的兩個(gè)狀態(tài)值。此時(shí),如果想要正確的在bar 里調(diào)用foo,就需要用到 yield* 表達(dá)式

yield* 表達(dá)式用來(lái)在一個(gè) Generator 函數(shù)里面 執(zhí)行 另一個(gè) Generator 函數(shù)

 function* foo() {
    yield 'aaa'
    yield 'bbb'
  }
  function* bar() {
    yield* foo()      // 在bar函數(shù)中 **執(zhí)行** foo函數(shù)
    yield 'ccc'
    yield 'ddd'
  }
  let iterator = bar()
  for(let value of iterator) {
    console.log(value)
  }
  // aaa
  // bbb
  // ccc
  // ddd

next() 方法的參數(shù)

yield表達(dá)式本身沒(méi)有返回值,或者說(shuō)總是返回undefined。next方法可以帶一個(gè)參數(shù),該參數(shù)就會(huì)被當(dāng)作上一個(gè)yield表達(dá)式的返回值

  function* gen(x) {
    let y = 2 * (yield (x + 1))   // 注意:yield 表達(dá)式如果用在另一個(gè)表達(dá)式中,必須放在圓括號(hào)里面
    let z = yield (y / 3)
    return x + y + z
  }
  let it = gen(5)
  /*** 正確的結(jié)果在這里 ***/
  console.log(it.next())  // 首次調(diào)用next,函數(shù)只會(huì)執(zhí)行到 “yield(5+1)” 暫停,并返回 {value: 6, done: false}
  console.log(it.next())  // 第二次調(diào)用next,沒(méi)有傳遞參數(shù),所以 y的值是undefined,那么 y/3 當(dāng)然是一個(gè)NaN,所以應(yīng)該返回 {value: NaN, done: false}
  console.log(it.next())  // 同樣的道理,z也是undefined,6 + undefined + undefined = NaN,返回 {value: NaN, done: true}

如果向next方法提供參數(shù),返回結(jié)果就完全不一樣了

{
  function* gen(x) {
    let y = 2 * (yield (x + 1))   // 注意:yield 表達(dá)式如果用在另一個(gè)表達(dá)式中,必須放在圓括號(hào)里面
    let z = yield (y / 3)
    return x + y + z
  }
  let it = gen(5)
  console.log(it.next())  // 正常的運(yùn)算應(yīng)該是先執(zhí)行圓括號(hào)內(nèi)的計(jì)算,再去乘以2,由于圓括號(hào)內(nèi)被 yield 返回 5 + 1 的結(jié)果并暫停,所以返回{value: 6, done: false}
  console.log(it.next(9))  // 上次是在圓括號(hào)內(nèi)部暫停的,所以第二次調(diào)用 next方法應(yīng)該從圓括號(hào)里面開(kāi)始,就變成了 let y = 2 * (9),y被賦值為18,所以第二次返回的應(yīng)該是 18/3的結(jié)果 {value: 6, done: false}
  console.log(it.next(2))  // 參數(shù)2被賦值給了 z,最終 x + y + z = 5 + 18 + 2 = 25,返回 {value: 25, done: true}
}

與 Iterator 接口的關(guān)系

ES6 規(guī)定,默認(rèn)的 Iterator 接口部署在數(shù)據(jù)結(jié)構(gòu)的Symbol.iterator屬性,或者說(shuō),一個(gè)數(shù)據(jù)結(jié)構(gòu)只要具有Symbol.iterator屬性,就可以認(rèn)為是“可遍歷的”(iterable)。

Symbol.iterator屬性本身是一個(gè)函數(shù),就是當(dāng)前數(shù)據(jù)結(jié)構(gòu)默認(rèn)的遍歷器生成函數(shù)。執(zhí)行這個(gè)函數(shù),就會(huì)返回一個(gè)遍歷器。

由于執(zhí)行 Generator 函數(shù)實(shí)際返回的是一個(gè)遍歷器,因此可以把 Generator 賦值給對(duì)象的Symbol.iterator屬性,從而使得該對(duì)象具有 Iterator 接口。

{
  let obj = {}
  function* gen() {
    yield 4
    yield 5
    yield 6
  }
  obj[Symbol.iterator] = gen
  for(let value of obj) {
    console.log(value)
  }
  // 4
  // 5
  // 6
  console.log([...obj])    // [4, 5, 6]
}

傳統(tǒng)對(duì)象沒(méi)有原生部署 Iterator接口,不能使用 for...of 和 擴(kuò)展運(yùn)算符,現(xiàn)在通過(guò)給對(duì)象添加Symbol.iterator 屬性和對(duì)應(yīng)的遍歷器生成函數(shù),就可以使用了

for...of 循環(huán) 

由于 Generator 函數(shù)運(yùn)行時(shí)生成的是一個(gè) Iterator 對(duì)象,因此,可以直接使用 for...of 循環(huán)遍歷,且此時(shí)無(wú)需再調(diào)用 next() 方法

這里需要注意,一旦 next() 方法的返回對(duì)象的 done 屬性為 true,for...of 循環(huán)就會(huì)終止,且不包含該返回對(duì)象

{
  function* gen() {
    yield 1
    yield 2
    yield 3
    yield 4
    return 5
  }
  for(let item of gen()) {
    console.log(item)
  }
  // 1 2 3 4
}

Generator.prototype.return()

Generator 函數(shù)返回的遍歷器對(duì)象,還有一個(gè) return 方法,可以返回給定的值(若沒(méi)有提供參數(shù),則返回值的value屬性為 undefined),并且 終結(jié) 遍歷 Generator 函數(shù)

{
  function* gen() {
    yield 1
    yield 2
    yield 3
  }
  let it = gen()
  it.next()             // {value: 1, done: false}
  it.return('ending')   // {value: "ending", done: true}
  it.next()             // {value: undefined, done: true}
}

Generator 函數(shù)應(yīng)用舉例

應(yīng)用一:假定某公司的年會(huì)上有一個(gè)抽獎(jiǎng)活動(dòng),總共6個(gè)人可以抽6次,每抽一次,抽獎(jiǎng)機(jī)會(huì)就會(huì)遞減

按照常規(guī)做法就需要聲明一個(gè)全局的變量來(lái)保存剩余的可抽獎(jiǎng)次數(shù),而全局變量會(huì)造成全局污染,指不定什么時(shí)候就被重新賦值了,所以往往并不被大家推薦

{
  let count = 6  // 聲明一個(gè)全局變量
  // 具體抽獎(jiǎng)邏輯的方法
  function draw() {
    // 執(zhí)行一段抽獎(jiǎng)邏輯
    // ...
    // 執(zhí)行完畢
    console.log(`剩余${count}次`)
  }
  // 執(zhí)行抽獎(jiǎng)的方法
  function startDrawing(){
    if(count > 0) {
      count--
      draw(count)
    }
  }
  let btn = document.createElement('button')
  btn.id = 'start'
  btn.textContent = '開(kāi)始抽獎(jiǎng)'
  document.body.appendChild(btn)
  document.getElementById('start').addEventListener('click', function(){
    startDrawing()
  }, false)
}

事實(shí)上,抽獎(jiǎng)通常是每個(gè)人自己來(lái)抽,每抽一次就調(diào)用一次抽獎(jiǎng)方法,而不是點(diǎn)一次就一次性就全部運(yùn)行完,是可暫停的,這個(gè)不就是 Generator 函數(shù)的意義所在嗎?

  // 具體抽獎(jiǎng)邏輯的方法
  function draw(count) {
    // 執(zhí)行一段抽獎(jiǎng)邏輯
    // ...
    console.log(`剩余${count}次`)
  }
  // 執(zhí)行抽獎(jiǎng)的方法
  function* remain(count) {
    while(count > 0) {
      count--
      yield draw(count)
    }
  }
  let startDrawing = remain(6)
  let btn = document.createElement('button')
  btn.id = 'start'
  btn.textContent = '開(kāi)始抽獎(jiǎng)'
  document.body.appendChild(btn)
  document.getElementById('start').addEventListener('click', function(){
    startDrawing.next()
  }, false)

應(yīng)用二:由于HTTP是一種無(wú)狀態(tài)協(xié)議,執(zhí)行一次請(qǐng)求后服務(wù)器無(wú)法記住是從哪個(gè)客戶端發(fā)起的請(qǐng)求,因此當(dāng)需要實(shí)時(shí)把服務(wù)器數(shù)據(jù)更新到客戶端時(shí)通常采用的方法是長(zhǎng)輪詢和Websocket。這里也可以用 Generator 函數(shù)來(lái)實(shí)現(xiàn)長(zhǎng)輪詢

{
  // 請(qǐng)求的方法
  function* ajax() {
    yield new Promise((resolve, reject) => {
      // 此處用一個(gè)定時(shí)器來(lái)模擬請(qǐng)求數(shù)據(jù)的耗時(shí),并約定當(dāng)返回的json中code為0表示有新數(shù)據(jù)更新
      setTimeout(() => {
        resolve({code: 0})
      }, 200)
    })
  }
  // 長(zhǎng)輪詢的方法
  function update() {
    let promise = ajax().next().value    // 返回的對(duì)象的value屬性是一個(gè) Promise 實(shí)例對(duì)象
    promise.then(res => {
      if(res.code != 0) {
        setTimeout(() => {
          console.log('2秒后繼續(xù)查詢.....')
          update()
        }, 2000)
      } else{
        console.log(res)
      }
    })
  }
  update()
}

以上就是JS Generator函數(shù)yield表達(dá)式示例詳解的詳細(xì)內(nèi)容,更多關(guān)于JS Generator函數(shù)yield表達(dá)式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

淳安县| 平舆县| 麟游县| 澄迈县| 河东区| 中牟县| 固原市| 西平县| 中超| 黄大仙区| 康乐县| 资阳市| 伊春市| 西和县| 台中县| 安阳县| 临潭县| 镇江市| 探索| 县级市| 墨脱县| 仙居县| 金乡县| 嫩江县| 广水市| 郁南县| 泸溪县| 新田县| 古田县| 桓仁| 城口县| 开封市| 太谷县| 穆棱市| 泽库县| 武宣县| 开原市| 钦州市| 客服| 枣庄市| 隆尧县|