从arrify到迭代器和生成器

74 阅读2分钟

一、源码阅读

简单看了这个方法的源码,他主要就是通过传递一个值,判断规则如下:

1.当为undefined 和 null这种的,返回一个空数组;

2.当为一个数组时,返回原值;

3.当为一个字符串时,将字符串放入空数组返回;

4.当为一个可迭代对象时,将可迭代对象使用展开运算符到一个数组中返回;

5.不满足以上条件的,都使用一个将值放入空数组中返回。

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];
}

二、拓展

这里面Symbol.iterator吸引了我的注意,从MDN上查到这个是给对象添加默认的迭代器。

const iterable1 = {};

iterable1[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

console.log([...iterable1]);
// Expected output: Array [1, 2, 3]

这里给iterable1添加了一个默认的迭代器,所以展开得到的属性就是1,2,3;而Symbol.iterator目前支持的就只有数组、字符串、set集合、map集合、TypeArray(类型数组)。

当看到这里那么function*这个表达式也引起我的注意,通过查询知道它是一个表达器,可以在内部定义生成器函数。

1.迭代器

简单解释为可以使用for...of来便利的对象,那么该迭代对象可以使用 ...展开运算符、结构赋值的方式。StringArrayTypedArrayMapSet 都是内置可迭代对象,因为它们的原型对象都拥有一个 Symbol.iterator 方法。

2.生成器

生成器是使用function*表达式编写,最初调用时,生成器函数不执行任何代码,而是返回一种称为 Generator 的迭代器,直到遇到yield关键字。

function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
    for (let i = start; i < end; i += step) {
        yield i;
    }
}
var a = makeRangeIterator(1,10,2)

a.next() // {value: 1, done: false}
a.next() // {value: 3, done: false}
a.next() // {value: 5, done: false}
a.next() // {value: 7, done: false}
a.next() // {value: 9, done: false}
a.next() // {value: undefined, done: true}

从上述代码中看出,当遇到yield关键字则生成器的给value赋值,当不满足的时候done返回的是true则终止,而且生成器有一个next方法,这个方法每次执行为上一次断点之后。

三、lssues处理

在看源码时,发现有人提出了这么一个问题,对象中定义了一个不规则的迭代器时,这个转化就会报错,这是因为typeof value[Symbol.iterator]判断不规则的迭代器也是function,所以会出现这个错误,MDN上也写了“如果一个迭代器 @@iterator 没有返回一个迭代器对象,那么它就是一个不符合标准的迭代器。这样的迭代器将会在运行期抛出异常,甚至出现非常诡异的 Bug”。

那么这里就考虑,如果它是一个不规则的迭代器,那么它就没有next方法,那么我们只需要判断它的next方法是不是一个function就行了。

改造后的源码如下:

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' && typeof value.next === "function") {
		return [...value];
	}

	return [value];
}
import arrify from "arrify";
let iterable = {  
  0: "a",  
  1: "b",  
  2: "c",  
  length: 3,  
  [Symbol.iterator]: () => 1,  
};

console.log(arrify(iterable));
// Expected output:[
  {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3,
    [Symbol(Symbol.iterator)]: [Function: [Symbol.iterator]] // 这里是直接放入这个迭代器
  }
]

四、总结

通过学习这段源码,源码其实很简单,但是这里就涉及到迭代器和生成器两块知识点,这块知识在实际项目开发中很少使用到,所以之前是一直知道这个概念,但是不知道是什么意思,通过这次学习也深化了解了这块的知识。