likes
comments
collection
share

es6:关于迭代器,你了解多少?本篇文章会通俗易懂的方式介绍迭代器,帮助友友们理解迭代器,并进一步剖析更深一层的知识点,

作者站长头像
站长
· 阅读数 22

一:遍历器 Iterator (迭代器)

  1. js中有多种可以被称之为集合的数据结构(arr, obj, set, map

  2. 我们希望某些数据结构是可以被迭代的,于是官方就打造了一个属性Iterator,并设定具有Iterator属性的数据结构就是可迭代的

  3. 迭代器属性的值必须是一个对象,且对象中必须拥有next方法,该next每次被调用,就会返回一个新对象 { done: false, value: x }

  4. 拥有迭代器属性的数据结构才可以被 for - of 遍历

  5. for of 遍历的其实是某结构上的迭代器对象

二:手写迭代器

下面带友友们实现一个简单的迭代器函数 createIterator,用来迭代一个数组 items,可以返回一个对象,该对象有一个 next 方法,每次调用该方法都会返回下一个元素的信息,直到数组中的所有元素都被遍历完

function createIterator(items) {
  var i = 0;
  return {
    next: function() {
      var done = i >= items.length;
      var value = !done ? items[i++] : undefined

      return {
        done: done,
        value: value
      }
    }
  }
}
var iterator = createIterator([1, 2, 3])

console.log(iterator.next()); // { done: false, value: 1 }
console.log(iterator.next()); // { done: false, value: 2 }
console.log(iterator.next()); // { done: false, value: 3 }
console.log(iterator.next()); // { done: true, value: undefined }
  1. 第一次调用 iterator.next() 时,i 为 0,因此 value 为 1,并且 done 为 false
  2. 第二次调用 iterator.next() 时,i 已经递增到 1,因此 value 为 2,并且 done 仍然为 false
  3. 第三次调用 iterator.next() 时,i 已经递增到 2,因此 value 为 3,并且 done 仍然为 false
  4. 第四次调用 iterator.next() 时,i 已经递增到 3,此时 i 大于等于数组长度,因此 done 为 true,而 value 为 undefined

三:对象迭代 VS 数组迭代

对象迭代

对于普通的 JavaScript 对象,它本身并不具备迭代能力,要使一个普通对象可迭代,需要在其原型链上定义 Symbol.iterator 方法,这个方法应该返回一个迭代器对象,该对象有一个 next 方法用于获取序列中的下一个元素。

function createIterator(items) {
  var i = 0;
  return {
    next: function() {
      var done = i >= items.length;
      var value = !done ? items[i++] : undefined;

      return {
        done: done,
        value: value
      };
    }
  };
}

// 定义一个普通对象
const obj = {
  value: 1
};

// 为 obj 添加一个 Symbol.iterator 属性,使其可迭代
obj[Symbol.iterator] = function() {
  return createIterator([1, 2, 3]);
};

// 使用 for...of 循环遍历 obj
for (let value of obj) {
  console.log(value);
}

这段代码的核心在于将一个迭代器绑定到了普通对象 objSymbol.iterator 属性上。这意味着尽管 obj 本身没有 [1, 2, 3] 这些值,但由于 Symbol.iterator 的定义,for...of 循环认为 obj 包含这些值,并按顺序遍历它们。但值得注意的一点是,这里的 obj 对象定义了一个 value 属性,但是在后续的代码中并没有使用到这个属性,这是因为定义了 objSymbol.iterator 属性后,for...of 循环将忽略 obj 中的其他属性,仅遍历由 Symbol.iterator 方法返回的迭代器所指定的值。

数组迭代

数组是一种原生支持迭代的数据结构。每个数组都有一个内置的 Symbol.iterator 方法,可以用来生成一个迭代器,用于遍历数组的元素。我们可以直接使用 for...of 循环来迭代数组。

const arr = [1, 2, 3];

for (let value of arr) {
  console.log(value);
}

比较

  • 原生支持:

    • 数组:数组是原生支持迭代的数据结构,可以直接使用 for...of 或者其他迭代机制。
    • 对象:普通对象本身不支持迭代,需要手动定义 Symbol.iterator 方法才能迭代。
  • 迭代方式:

    • 数组:数组的 Symbol.iterator 返回一个迭代器,该迭代器按顺序返回数组中的每个元素。
    • 对象:对于普通对象,你需要定义自己的逻辑来决定如何迭代其属性或值。

四:手写for of

上面讲到数组是原生支持迭代的,可以直接使用 for...of ,那么这个方法是怎么找到的呢?现在带友友们解析一下

  1. 检查对象是否可迭代:

    • if (obj[Symbol.iterator]) {: 检查 obj 是否具有 Symbol.iterator 属性。
    • throw new TypeError(obj + " is not iterable");: 如果对象有 Symbol.iterator 属性,则抛出一个 TypeError,这是因为函数预期接收不可迭代对象作为参数,但接收到的是一个可迭代对象。
  2. 创建迭代器并开始迭代:

    • let iterator = obj[Symbol.iterator]();: 获取 obj 的迭代器。
    • let res = iterator.next();: 调用迭代器的 next 方法以获取第一个迭代结果。
  3. 迭代过程:

    • while (!res.done) {: 只要迭代结果的 done 属性为 false,循环将继续。
    • cb(res.value);: 调用回调函数 cb 并传递当前迭代项的值。
    • res = iterator.next();: 获取下一个迭代结果。
function forOf(obj, cb) {
  if (obj[Symbol.iterator]) {
    throw new TypeError(obj + " is not iterable")
  }
  let iterator = obj[Symbol.iterator]()
  let res = iterator.next()
  while (!res.done) {
    cb(res.value)
    res = iterator.next()
  }
}
var colors = ['red', 'green', 'blue']

forOf(colors, function(value) {
  console.log(value);
})

五:字节面试题

让以下代码成立:var [a, b] = {a: 1, b: 2}

在这段代码中,{a: 1, b: 2} 是一个普通的 JS 对象。在默认情况下,对象不能被解构为变量列表。也就是说,var [a, b] = {a: 1, b: 2}; 这样的写法会导致语法错误。为了使其成立,我们需要使对象变得可迭代,并且确保迭代顺序符合我们的期望。一种方法是定义对象的 Symbol.iterator 方法,让它返回一个迭代器,并且该迭代器可以按照我们期望的顺序返回对象的值。

解答:

Object.prototype[Symbol.iterator] = function() {
  return Object.values(this)[Symbol.iterator]()
}

var [a, b] = {a: 1, b: 2}

解析:

Object.prototypeSymbol.iterator 属性定义为一个函数。该函数返回调用 Object.values(this) 所得数组的迭代器。这里的关键点是:

  • Object.values(this) 返回一个包含对象自身所有可枚举属性值的数组。
  • [Symbol.iterator]() 调用返回的数组的迭代器。

这意味着现在所有的对象都可以通过 for...of 循环或其他迭代机制进行迭代。

转载自:https://juejin.cn/post/7401408756221542426
评论
请登录