自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

JavaScript Lazy evaluation:可迭代對(duì)象與迭代器

開發(fā) 前端
Lazy evaluation常被譯為“延遲計(jì)算”或“惰性計(jì)算”,指的是僅僅在真正需要執(zhí)行的時(shí)候才計(jì)算表達(dá)式的值。

[[353766]]

本文已經(jīng)過原作者 MelkorNemesis 授權(quán)翻譯。

Lazy evaluation

Lazy evaluation常被譯為“延遲計(jì)算”或“惰性計(jì)算”,指的是僅僅在真正需要執(zhí)行的時(shí)候才計(jì)算表達(dá)式的值。

與惰性求值相反的是及早求值(eager evaluation)及早求值,也被稱為貪婪求值(greedy evaluation)或嚴(yán)格求值,是多數(shù)傳統(tǒng)編程語(yǔ)言的求值策略。

充分利用惰性求值的特性帶來的好處主要體現(xiàn)在以下兩個(gè)方面:

  • 避免不必要的計(jì)算,帶來性能上的提升。
  • 節(jié)省空間,使得無限循環(huán)的數(shù)據(jù)結(jié)構(gòu)成為可能。

迭代器

ES6 中的迭代器使惰性求值和創(chuàng)建用戶定義的數(shù)據(jù)序列成為可能。迭代是一種遍歷數(shù)據(jù)的機(jī)制。迭代器是用于遍歷數(shù)據(jù)結(jié)構(gòu)元素(稱為Iterable)的指針,用于產(chǎn)生值序列的指針。

迭代器是一個(gè)可以被迭代的對(duì)象。它抽象了數(shù)據(jù)容器,使其行為類似于可迭代對(duì)象。

迭代器在實(shí)例化時(shí)不計(jì)算每個(gè)項(xiàng)目的值,僅在請(qǐng)求時(shí)才生成下一個(gè)值。這非常有用,特別是對(duì)于大型數(shù)據(jù)集或無限個(gè)元素的序列。

可迭代對(duì)象

可迭代對(duì)象是希望其元素可被公眾訪問的數(shù)據(jù)結(jié)構(gòu)。JS 中的很多對(duì)象都是可迭代的,它們可能不是很好的察覺,但是如果仔細(xì)檢查,就會(huì)發(fā)現(xiàn)迭代的特征:

  • new Map([iterable])
  • new WeakMap([iterable])
  • new Set([iterable])
  • new WeakSet([iterable])
  • Promise.all([iterable])
  • Promise.race([iterable])
  • Array.from([iterable])

還有需要一個(gè)可迭代的對(duì)象,否則,它將拋出一個(gè)類型錯(cuò)誤,例如:

  • for ... of
  • ... (展開操作符)const [a, b, ..] = iterable (解構(gòu)賦值)
  • yield* (生成器)

JavaScript中已有許多內(nèi)置的可迭代項(xiàng):

String,Array,TypedArray,Map,Set。

迭代協(xié)議

迭代器和可迭對(duì)象遵循迭代協(xié)議。

協(xié)議是一組接口,并規(guī)定了如何使用它們。

迭代器遵循迭代器協(xié)議,可迭代遵循可迭代協(xié)議。

可迭代的協(xié)議

要使對(duì)象變得可迭代,它必須實(shí)現(xiàn)一個(gè)通過Symbol.iterator的迭代器方法,這個(gè)方法是迭代器的工廠。

使用 TypeScript,可迭代協(xié)議如下所示:

  1. interface Iterable { 
  2.   [Symbol.iterator]() : Iterator; 

Symbol.iterator]()是無參數(shù)函數(shù)。在可迭代對(duì)象上調(diào)用它,這意味著我們可以通過this來訪問可迭代對(duì)象,它可以是常規(guī)函數(shù)或生成器函數(shù)。

迭代器協(xié)議

迭代器協(xié)議定義了產(chǎn)生值序列的標(biāo)準(zhǔn)方法。

為了使對(duì)象成為迭代器,它必須實(shí)現(xiàn)next()方法。迭代器可以實(shí)現(xiàn)return()方法,我們將在本文后面討論這個(gè)問題。

使用 TypeScript,迭代器協(xié)議如下所示:

  1. interface Iterator { 
  2.     next() : IteratorResult; 
  3.     return?(value?: any): IteratorResult; 

IteratorResult 的定義如下:

  1. interface IteratorResult { 
  2.     value?: any
  3.     done: boolean; 
  • done通知消費(fèi)者迭代器是否已經(jīng)被使用,false表示仍有值需要生成,true表示迭代器已經(jīng)結(jié)束。
  • value 可以是任何 JS 值,它是向消費(fèi)者展示的值。

當(dāng)done為true時(shí),可以省略value。

組合

迭代器和可以可迭代對(duì)象可以用下面這張圖來表示:

事例

基礎(chǔ)知識(shí)介紹完了,接著,我們來配合一些事例來加深我們的映像。

范圍迭代器

我們先從一個(gè)非常基本的迭代器開始,createRangeIterator迭代器。

我們手動(dòng)調(diào)用it.next()以獲得下一個(gè)IteratorResult。最后一次調(diào)用返回{done:true},這意味著迭代器現(xiàn)在已被使用,不再產(chǎn)生任何值。

  1. function createRangeIterator(fromto) { 
  2.   let i = from
  3.  
  4.   return { 
  5.     next() { 
  6.       if (i <= to) { 
  7.         return { value: i++, done: false }; 
  8.       } else { 
  9.         return { done: true }; 
  10.       } 
  11.     } 
  12.   } 
  13.  
  14. const it = createRangeIterator(1, 3); 
  15.  
  16. console.log(it.next()); 
  17. console.log(it.next()); 
  18. console.log(it.next()); 
  19. console.log(it.next()); 

可迭代范圍迭代器

在本文的前面,我已經(jīng)提到 JS 中的某些語(yǔ)句需要一個(gè)可迭代的對(duì)象。因此,我們前面的示例在與for ... of循環(huán)一起使用時(shí)將不起作用。

但是創(chuàng)建符合迭代器和可迭代協(xié)議的對(duì)象非常容易。

  1. function createRangeIterator (fromto) { 
  2.   let i = from 
  3.  
  4.   return { 
  5.     [Symbol.iterator] () { 
  6.       return this 
  7.     }, 
  8.     next() { 
  9.       if (i <= to) { 
  10.         return { value: i++, done: false } 
  11.       } else { 
  12.         return { done: true } 
  13.       } 
  14.     } 
  15.   } 
  16.  
  17. const it = createRangeIterator(1, 3) 
  18.  
  19. for (const i of it) { 
  20.   console.log(i) 

無限序列迭代器

迭代器可以表示無限制大小的序列,因?yàn)樗鼈儍H在需要時(shí)才計(jì)算值。

注意不要在無限迭代器上使用擴(kuò)展運(yùn)算符(...),JS 將嘗試消費(fèi)迭代器,由于迭代器是無限的,因此它將永遠(yuǎn)不會(huì)結(jié)束。所以你的應(yīng)用程序?qū)⒈罎?,因?yàn)閮?nèi)存已被耗盡 ??

同樣,for ... of 循環(huán)也是一樣的情況,所以要確保能退出循環(huán):

  1. function createEvenNumbersIterator () { 
  2.   let value = 0 
  3.  
  4.   return { 
  5.     [Symbol.iterator] () { 
  6.       return this 
  7.     }, 
  8.     next () { 
  9.       value += 2 
  10.       return { value, done: false
  11.     } 
  12.   } 
  13.  
  14. const it = createEvenNumbersIterator() 
  15.  
  16. const [a, b, c] = it 
  17. console.log({a, b, c}) 
  18.  
  19. const [x, y, z] = it 
  20. console.log({ x, y, z }) 
  21.  
  22. for (const even of it) { 
  23.   console.log(even) 
  24.   if (even > 20) { 
  25.     break 
  26.   } 

關(guān)閉迭代器

前面我們提到過,迭代器可以有選擇地使用return()方法。當(dāng)?shù)髦钡阶詈蠖紱]有迭代時(shí)使用此方法,并讓迭代器進(jìn)行清理。

for ... of循環(huán)可以通過以下方式更早地終止迭代:

  • break
  • continue
  • throw
  • return
  1. function createCloseableIterator () { 
  2.   let idx = 0 
  3.   const data = ['a''b''c''d''e'
  4.  
  5.   function cleanup() { 
  6.     console.log('Performing cleanup'
  7.   } 
  8.   return { 
  9.     [Symbol.iterator]() { return this }, 
  10.     next () { 
  11.       if (idx <= data.length - 1) { 
  12.         return { value: data[idx++], done: false } 
  13.       } else { 
  14.         cleanup() 
  15.         return { done: true } 
  16.       } 
  17.     }, 
  18.     return () { 
  19.       cleanup() 
  20.       return { done: true } 
  21.     } 
  22.   } 
  23.  
  24. const it = createCloseableIterator() 
  25.  
  26. for (const value of it) { 
  27.   console.log(value) 
  28.   if (value === 'c') { 
  29.     break 
  30.   } 
  31.  
  32. console.log('\n----------\n'
  33.  
  34. const _it = createCloseableIterator(); 
  35. for (const value of _it) { 
  36.   console.log(value); 

  • 如果知道迭代器已經(jīng)結(jié)束,則手動(dòng)調(diào)用cleanup()函數(shù)。
  • 如果突然完成,則return()起作用并為我們進(jìn)行清理。

額外的內(nèi)容

如果你已經(jīng)做到了這一點(diǎn),我們來看看一些額外的內(nèi)容。

組合器

組合器是將現(xiàn)有可迭代對(duì)象組合在一起以創(chuàng)建新可迭代對(duì)象的函數(shù)。

因此,我們能夠創(chuàng)建許多實(shí)用函數(shù)。那map或者filter呢?看看下面的代碼,花一分鐘時(shí)間來理解它。

  1. function createEvenNumbersIterator() { 
  2.   let value = 0; 
  3.  
  4.   return { 
  5.     [Symbol.iterator]() { 
  6.       return this; 
  7.     }, 
  8.     next() { 
  9.       value += 2; 
  10.       return { value, done: false }; 
  11.     } 
  12.   } 
  13.  
  14. function map(fn, iterable) { 
  15.   const iter = iterable[Symbol.iterator](); 
  16.  
  17.   return { 
  18.     [Symbol.iterator]() { 
  19.       return this; 
  20.     }, 
  21.     next() { 
  22.       const n = iter.next(); 
  23.       if (!n.done) { 
  24.         return { value: fn(n.value), done: false }; 
  25.       } else { 
  26.         return { done: true }; 
  27.       } 
  28.     } 
  29.   } 
  30.  
  31. function filter(fn, iterable) { 
  32.   const iter = iterable[Symbol.iterator](); 
  33.  
  34.   return { 
  35.     [Symbol.iterator]() { 
  36.       return this; 
  37.     }, 
  38.     next() { 
  39.       const n = iter.next(); 
  40.       if (!n.done) { 
  41.         if (fn(n.value)) { 
  42.           return { value: n.value, done: false }; 
  43.         } else { 
  44.           return this.next(); 
  45.         } 
  46.       } else { 
  47.         return { done: true }; 
  48.       } 
  49.     } 
  50.   } 
  51.  
  52. function take(n, iterable) { 
  53.   const iter = iterable[Symbol.iterator](); 
  54.  
  55.   return { 
  56.     [Symbol.iterator]() { 
  57.       return this; 
  58.     }, 
  59.     next() { 
  60.       if (n > 0) { 
  61.         n--; 
  62.         return iter.next(); 
  63.       } else { 
  64.         return { done: true }; 
  65.       } 
  66.     } 
  67.   } 
  68.  
  69. function cycle(iterable) { 
  70.   const iter = iterable[Symbol.iterator](); 
  71.   const saved = []; 
  72.   let idx = 0; 
  73.    
  74.   return { 
  75.     [Symbol.iterator]() { 
  76.       return this; 
  77.     }, 
  78.     next() { 
  79.       const n = iter.next(); 
  80.       if (!n.done) { 
  81.         saved[idx++] = n.value; 
  82.         return { value: n.value, done: false }; 
  83.       } else { 
  84.         return { value: saved[idx++ % saved.length], done: false }; 
  85.       } 
  86.     } 
  87.   } 
  88.  
  89. function collect(iterable) { 
  90.   // consumes the iterator 
  91.   return Array.from(iterable); 
  92.  
  93. const evenNumbersIterator = createEvenNumbersIterator(); 
  94. const result = collect(                 // 7. and collect the result 
  95.   filter(                               // ⬆️ 6. keep only values higher than 1 
  96.     val => val > 1, map(                // ⬆️ 5. divide obtained values by 2 
  97.       val => val / 2, take(             // ⬆️ 4. take only six of them 
  98.         6, cycle(                       // ⬆️ 3. make an infinite cycling sequence of them 
  99.           take(                         // ⬆️ 2. take just three of them 
  100.             3, evenNumbersIterator      // ⬆️ 1. infinite sequence of even numbers 
  101.           ) 
  102.         ) 
  103.       ) 
  104.     ) 
  105.   ) 
  106. ); 
  107.  
  108. console.log(result); 

這是一大堆代碼,很快我們將看到如何使用生成器和函數(shù)式編程概念來重構(gòu)所有這些內(nèi)容。保持關(guān)注,并注意我的后續(xù)文章,我們?nèi)匀挥泻芏鄡?nèi)容要講。

作者:MelkorNemesis 譯者:前端小智 來源:medium

原文:https://medium.com/@MelrNemesis/javascript-lazy-evaluation-iterables-iterators-e0770a5de96f

本文轉(zhuǎn)載自微信公眾號(hào)「 大遷世界」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 大遷世界公眾號(hào)。

 

責(zé)任編輯:武曉燕 來源: 大遷世界
相關(guān)推薦

2022-10-26 08:25:06

Python編程迭代器

2021-11-28 08:03:41

Python迭代器對(duì)象

2021-09-14 07:26:25

JavaScript迭代對(duì)象

2021-09-03 10:00:00

JavaScript迭代對(duì)象

2024-11-14 13:15:22

JavaScript可迭代數(shù)組

2025-03-26 10:56:54

2022-01-15 10:01:15

Javascript 高階函數(shù)前端

2017-06-26 16:26:15

Python迭代對(duì)象迭代器

2024-03-25 08:57:49

模塊迭代對(duì)象迭代器

2016-03-28 10:39:05

Python迭代迭代器

2022-10-24 13:02:38

2024-02-27 00:10:06

語(yǔ)言Javascript

2013-08-19 16:44:15

.Net

2023-03-01 00:07:32

JavaScript迭代器生成器

2009-12-15 14:58:19

Ruby迭代器

2011-07-13 13:56:06

STL迭代器

2022-10-26 09:27:59

Python編程迭代器協(xié)議

2020-11-06 09:01:46

迭代器模式

2009-08-11 13:59:41

迭代器模式C# Iterator

2009-08-26 16:26:37

C#迭代器模式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)