文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

JavaScript Lazy evaluation:可迭代对象与迭代器

2024-12-03 16:47

关注

本文已经过原作者 MelkorNemesis 授权翻译。

Lazy evaluation

Lazy evaluation常被译为“延迟计算”或“惰性计算”,指的是仅仅在真正需要执行的时候才计算表达式的值。

与惰性求值相反的是及早求值(eager evaluation)及早求值,也被称为贪婪求值(greedy evaluation)或严格求值,是多数传统编程语言的求值策略。

充分利用惰性求值的特性带来的好处主要体现在以下两个方面:

迭代器

ES6 中的迭代器使惰性求值和创建用户定义的数据序列成为可能。迭代是一种遍历数据的机制。迭代器是用于遍历数据结构元素(称为Iterable)的指针,用于产生值序列的指针。

迭代器是一个可以被迭代的对象。它抽象了数据容器,使其行为类似于可迭代对象。

迭代器在实例化时不计算每个项目的值,仅在请求时才生成下一个值。这非常有用,特别是对于大型数据集或无限个元素的序列。

可迭代对象

可迭代对象是希望其元素可被公众访问的数据结构。JS 中的很多对象都是可迭代的,它们可能不是很好的察觉,但是如果仔细检查,就会发现迭代的特征:

还有需要一个可迭代的对象,否则,它将抛出一个类型错误,例如:

JavaScript中已有许多内置的可迭代项:

String,Array,TypedArray,Map,Set。

迭代协议

迭代器和可迭对象遵循迭代协议。

协议是一组接口,并规定了如何使用它们。

迭代器遵循迭代器协议,可迭代遵循可迭代协议。

可迭代的协议

要使对象变得可迭代,它必须实现一个通过Symbol.iterator的迭代器方法,这个方法是迭代器的工厂。

使用 TypeScript,可迭代协议如下所示:

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

Symbol.iterator]()是无参数函数。在可迭代对象上调用它,这意味着我们可以通过this来访问可迭代对象,它可以是常规函数或生成器函数。

迭代器协议

迭代器协议定义了产生值序列的标准方法。

为了使对象成为迭代器,它必须实现next()方法。迭代器可以实现return()方法,我们将在本文后面讨论这个问题。

使用 TypeScript,迭代器协议如下所示:

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

IteratorResult 的定义如下:

  1. interface IteratorResult { 
  2.     value?: any
  3.     done: boolean; 

当done为true时,可以省略value。

组合

迭代器和可以可迭代对象可以用下面这张图来表示:

事例

基础知识介绍完了,接着,我们来配合一些事例来加深我们的映像。

范围迭代器

我们先从一个非常基本的迭代器开始,createRangeIterator迭代器。

我们手动调用it.next()以获得下一个IteratorResult。最后一次调用返回{done:true},这意味着迭代器现在已被使用,不再产生任何值。

  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()); 

可迭代范围迭代器

在本文的前面,我已经提到 JS 中的某些语句需要一个可迭代的对象。因此,我们前面的示例在与for ... of循环一起使用时将不起作用。

但是创建符合迭代器和可迭代协议的对象非常容易。

  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) 

无限序列迭代器

迭代器可以表示无限制大小的序列,因为它们仅在需要时才计算值。

注意不要在无限迭代器上使用扩展运算符(...),JS 将尝试消费迭代器,由于迭代器是无限的,因此它将永远不会结束。所以你的应用程序将崩溃,因为内存已被耗尽 ??

同样,for ... of 循环也是一样的情况,所以要确保能退出循环:

  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.   } 

关闭迭代器

前面我们提到过,迭代器可以有选择地使用return()方法。当迭代器直到最后都没有迭代时使用此方法,并让迭代器进行清理。

for ... of循环可以通过以下方式更早地终止迭代:

  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); 

额外的内容

如果你已经做到了这一点,我们来看看一些额外的内容。

组合器

组合器是将现有可迭代对象组合在一起以创建新可迭代对象的函数。

因此,我们能够创建许多实用函数。那map或者filter呢?看看下面的代码,花一分钟时间来理解它。

  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); 

这是一大堆代码,很快我们将看到如何使用生成器和函数式编程概念来重构所有这些内容。保持关注,并注意我的后续文章,我们仍然有很多内容要讲。

作者:MelkorNemesis 译者:前端小智 来源:medium

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

本文转载自微信公众号「 大迁世界」,可以通过以下二维码关注。转载本文请联系 大迁世界公众号。

 

来源:大迁世界内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯