乐趣区

关于javascript:JavaScript-中的可迭代对象与迭代器是啥

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

有幻想,有干货,微信搜寻 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。

Lazy evaluation

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

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

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

  • 防止不必要的计算,带来性能上的晋升。
  • 节俭空间,使得有限循环的数据结构成为可能。

迭代器

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

迭代器 是一个能够被迭代的对象。它形象了数据容器,使其行为相似于可迭代对象。

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

可迭代对象

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

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

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

  • for ... of
  • ... (开展操作符)
    const [a, b, ..] = iterable (解构赋值)
  • yield* (生成器)

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

String,Array,TypedArray,Map,Set

迭代协定

迭代器和可迭对象遵循 迭代协定

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

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

可迭代的协定

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

应用 TypeScript,可迭代协定如下所示:

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

Symbol.iterator]()是无参数函数。在可迭代对象上调用它,这意味着咱们能够通过 this 来拜访可迭代对象,它能够是惯例函数或生成器函数。

迭代器协定

迭代器协定定义了产生值序列的规范办法。

为了使对象成为迭代器,它必须实现 next() 办法。迭代器能够实现 return() 办法,咱们将在本文前面探讨这个问题。

应用 TypeScript,迭代器协定如下所示:

interface Iterator {next() : IteratorResult;
    return?(value?: any): IteratorResult;
}

IteratorResult 的定义如下:

interface IteratorResult {
    value?: any;
    done: boolean;
}
  • done告诉消费者迭代器是否曾经被应用,false示意仍有值须要生成,true示意迭代器曾经完结。
  • value 能够是任何 JS 值,它是向消费者展现的值。

donetrue时,能够省略value

组合

迭代器和能够可迭代对象能够用上面这张图来示意:

事例

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

范畴迭代器

咱们先从一个十分根本的迭代器开始,createRangeIterator迭代器。

咱们手动调用 it.next() 以取得下一个IteratorResult。最初一次调用返回{done:true},这意味着迭代器当初已被应用,不再产生任何值。

function createRangeIterator(from, to) {
  let i = from;

  return {next() {if (i <= to) {return { value: i++, done: false};
      } else {return { done: true};
      }
    }
  }
}

const it = createRangeIterator(1, 3);

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

可迭代范畴迭代器

在本文的后面,我曾经提到 JS 中的某些语句须要一个可迭代的对象。因而,咱们后面的示例在与 for ... of 循环一起应用时将不起作用。

然而创立合乎 迭代器 可迭代协定 的对象非常容易。

function createRangeIterator (from, to) {
  let i = from

  return {[Symbol.iterator] () {return this},
    next() {if (i <= to) {return { value: i++, done: false}
      } else {return { done: true}
      }
    }
  }
}

const it = createRangeIterator(1, 3)

for (const i of it) {console.log(i)
}

有限序列迭代器

迭代器能够示意无限度大小的序列,因为它们仅在须要时才计算值。

留神不要在有限迭代器上应用扩大运算符(...),JS 将尝试生产迭代器,因为迭代器是有限的,因而它将永远不会完结。所以你的应用程序将解体,因为内存已被耗尽 😱

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

function createEvenNumbersIterator () {
  let value = 0

  return {[Symbol.iterator] () {return this},
    next () {
      value += 2
      return {value, done: false}
    }
  }
}

const it = createEvenNumbersIterator()

const [a, b, c] = it
console.log({a, b, c})

const [x, y, z] = it
console.log({x, y, z})

for (const even of it) {console.log(even)
  if (even > 20) {break}
}

敞开迭代器

后面咱们提到过,迭代器能够有选择地应用 return() 办法。当迭代器直到最初都没有迭代时应用此办法,并让迭代器进行清理。

for ... of循环能够通过以下形式更早地终止迭代:

  • break
  • continue
  • throw
  • return
function createCloseableIterator () {
  let idx = 0
  const data = ['a', 'b', 'c', 'd', 'e']

  function cleanup() {console.log('Performing cleanup')
  }
  return {[Symbol.iterator]() { return this},
    next () {if (idx <= data.length - 1) {return { value: data[idx++], done: false }
      } else {cleanup()
        return {done: true}
      }
    },
    return () {cleanup()
      return {done: true}
    }
  }
}

const it = createCloseableIterator()

for (const value of it) {console.log(value)
  if (value === 'c') {break}
}

console.log('\n----------\n')

const _it = createCloseableIterator();
for (const value of _it) {console.log(value);
}

  • 如果晓得迭代器曾经完结,则手动调用 cleanup() 函数。
  • 如果忽然实现,则 return() 起作用并为咱们进行清理。

💥 额定的内容

如果你曾经做到了这一点,咱们来看看一些额定的内容。

组合器

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

因而,咱们可能创立许多实用函数。那 map 或者 filter 呢? 看看上面的代码,花一分钟工夫来了解它。

function createEvenNumbersIterator() {
  let value = 0;

  return {[Symbol.iterator]() {return this;},
    next() {
      value += 2;
      return {value, done: false};
    }
  }
}

function map(fn, iterable) {const iter = iterable[Symbol.iterator]();

  return {[Symbol.iterator]() {return this;},
    next() {const n = iter.next();
      if (!n.done) {return { value: fn(n.value), done: false };
      } else {return { done: true};
      }
    }
  }
}

function filter(fn, iterable) {const iter = iterable[Symbol.iterator]();

  return {[Symbol.iterator]() {return this;},
    next() {const n = iter.next();
      if (!n.done) {if (fn(n.value)) {return { value: n.value, done: false};
        } else {return this.next();
        }
      } else {return { done: true};
      }
    }
  }
}

function take(n, iterable) {const iter = iterable[Symbol.iterator]();

  return {[Symbol.iterator]() {return this;},
    next() {if (n > 0) {
        n--;
        return iter.next();} else {return { done: true};
      }
    }
  }
}

function cycle(iterable) {const iter = iterable[Symbol.iterator]();
  const saved = [];
  let idx = 0;
  
  return {[Symbol.iterator]() {return this;},
    next() {const n = iter.next();
      if (!n.done) {saved[idx++] = n.value;
        return {value: n.value, done: false};
      } else {return { value: saved[idx++ % saved.length], done: false };
      }
    }
  }
}

function collect(iterable) {
  // consumes the iterator
  return Array.from(iterable);
}

const evenNumbersIterator = createEvenNumbersIterator();
const result = collect(                 // 7. and collect the result
  filter(                               // ⬆️ 6. keep only values higher than 1
    val => val > 1, map(                // ⬆️ 5. divide obtained values by 2
      val => val / 2, take(             // ⬆️ 4. take only six of them
        6, cycle(                       // ⬆️ 3. make an infinite cycling sequence of them
          take(                         // ⬆️ 2. take just three of them
            3, evenNumbersIterator      // ⬆️ 1. infinite sequence of even numbers
          )
        )
      )
    )
  )
);

console.log(result);

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


代码部署后可能存在的 BUG 没法实时晓得,预先为了解决这些 BUG,花了大量的工夫进行 log 调试,这边顺便给大家举荐一个好用的 BUG 监控工具 Fundebug。

原文:https://medium.com/@MelrNemes…

交换

文章每周继续更新,能够微信搜寻「大迁世界」第一工夫浏览和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 曾经收录,整顿了很多我的文档,欢送 Star 和欠缺,大家面试能够参照考点温习,另外关注公众号,后盾回复 福利,即可看到福利,你懂的。

退出移动版