通过 arrify 源码分析浅析迭代器

111 阅读2分钟

本文参加了由公众号@若川视野 发起的每周源码共读活动,    点击了解详情一起参与。

【若川视野 x 源码共读】第33期 | # arrify 转数组点击了解本期详情一起参与

1. arrify 源码浅析

先从 github 仓库把 arrify 项目拉下来。

arrify 函数代码就 19 行。

export default function arrify(value) {
 if (value === null || value === undefined) {
  return [];
 }

 if (Array.isArray(value)) {
  return value;
 }

 if (typeof value === 'string') {
  return [value];
 }

 if (typeof value[Symbol.iterator] === 'function') {
  return [...value];
 }

 return [value];
}

功能就是把第一个参数值转化为数组。

  1. null 或 undefined 就返回空数组。
  2. 如果是数组,就返回数组本身。
  3. 如果是字符串,就返回包含一个字符串值的数组。
  4. 如果实现了迭代器接口,就通过展开运算符转换为数组。
  5. 以上4种情况都不是,则返回包含第一个参数值的数组。

2. 迭代器本质

Symbol.iterator 属性值是一个特殊的值,如果一个对象的 Symbol.iterator 属性值是一个函数,则表示这个对象是可迭代的。就是可以通过 for...of 就行遍历。

比如数组本身就实现了这个接口,说明数组是可以迭代的。

let arr = [2, 3, 4]
for (let arrItem of arr) {
 console.log(arrItem)
 // 2
 // 3
 // 4
}

既然这个 Symbol.iterator 属性值是一个函数,那我们执行一下,实际上是通过 next 函数来获取下一个迭代值,直到迭代的值的 done 属性为 true 表示迭代完成了。

let arrIt = arr[Symbol.iterator]()
console.log(arrIt.next())
// { value: 2, done: false }

console.log(arrIt.next())
// { value: 3, done: false }

console.log(arrIt.next())
// { value: 4, done: false }

console.log(arrIt.next())
// { value: undefined, done: true }

3. Object类型模拟迭代器

像数组,字符串,Map对象,Set对象都实现了 Symbol.iterator 接口,Object 是没有实现的,我们检验一下。

let obj = {a: 11, b: 22, c: 33}
for (let oi of obj) {
   console.log(oi)
   //  obj is not iterable
}

报错,确实, Object 类型是不可迭代的,也用不了 for...of ,那就用 obj 模拟实现 Symbol.iterator 接口。

obj[Symbol.iterator] = function() {
 let objKeys = Object.keys(this)
 let curIndex = 0
 return {
  next: () => {
   //  如果属性还没有迭代完
   if (curIndex >= objKeys.length) {
    return { value: undefined, done: true }
   }
   return { value: this[objKeys[curIndex++]], done: false}
  }
 }
}

let objIt = obj[Symbol.iterator]()
console.log(objIt.next())
//  { value: 11, done: false }

console.log(objIt.next())
//  { value: 22, done: false }

console.log(objIt.next())
// { value: 33, done: false }

console.log(objIt.next())
// { value: undefined, done: true }

实现的效果和预想的一样,那能不能有 for...of 语法呢?

for (let oi of obj) {
 console.log(oi)
 // 11
 // 22
 // 33
}

可以,运行很正常,结果也和预想的一致。