本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
【若川视野 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];
}
功能就是把第一个参数值转化为数组。
- null 或 undefined 就返回空数组。
- 如果是数组,就返回数组本身。
- 如果是字符串,就返回包含一个字符串值的数组。
- 如果实现了迭代器接口,就通过展开运算符转换为数组。
- 以上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
}
可以,运行很正常,结果也和预想的一致。