我是靠谱客的博主 懦弱奇异果,最近开发中收集的这篇文章主要介绍解读 arrify 源码,Symbol.iterator,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

arrify是做什么的?

通俗的讲,就是把数据转换乘数组类型。

源码

大家可以先了解一下源码,很简单:

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

逻辑基本上都很基础,基本上就是判断传入的数据类型,然后根据不同类型转换为数据,然后return出去。在逻辑的最后有一个知识点,Symbol.iterator 迭代器,下面给大家讲解一下此属性。

Symbol.iterator 迭代器

首先先给大家说一下这段代码逻辑的作用:

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

这块的逻辑是判断当前传入的数据原型中,是否存在Symbol.iterator 方法,如果存在就证明可以做遍历及使用拓展运算符进行类型转化,retrun 出一个数组。

iterator(遍历器)的概念?

引用阮一峰大佬的话:

JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了MapSet。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是MapMap的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。

遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

详细资料:ES6 入门教程

以上就是对于迭代器的解释,理解了就可以到这里了,如果不理解或者没时间看资料的,大家可以往下继续看。

首先我们要知道原生支持iterator的数据结构如下:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

细心的应该也发现了,这里面没有对象类型,这个后面在说具体原因。

下面我们拿Array类型的iterator属性举例:

let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

上面代码中,变量arr是一个数组,原生就具有遍历器接口,部署在arrSymbol.iterator属性上面。所以,调用这个属性,就得到遍历器对象。

也就是说可以直接使用for...of进行遍历或者扩展运算符等。

那为什么没有在object上实现iterator属性呢?

对象(Object)之所以没有默认部署 Iterator 接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。本质上,遍历器是一种线性处理,对于任何非线性的数据结构,部署遍历器接口,就等于部署一种线性转换。不过,严格地说,对象部署遍历器接口并不是很必要,因为这时对象实际上被当作 Map 结构使用,ES5 没有 Map 结构,而 ES6 原生提供了。

那有什么方法可以让对象使用for...of遍历呢?

那就必须在Symbol.iterator的属性上部署遍历器生成方法(原型链上的对象具有该方法也可)。

class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
}
[Symbol.iterator]() { return this; }
next() {
var value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
}
return {done: true, value: undefined};
}
}
function range(start, stop) {
return new RangeIterator(start, stop);
}
for (var value of range(0, 3)) {
console.log(value); // 0, 1, 2
}

那说了半天iterator,什么场景下会调用呢?

(1)解构赋值

对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法。

let set = new Set().add('a').add('b').add('c');
let [x,y] = set;
// x='a'; y='b'
let [first, ...rest] = set;
// first='a'; rest=['b','c'];

(2)扩展运算符

扩展运算符(...)也会调用默认的 Iterator 接口。

// 例一
var str = 'hello';
[...str] //
['h','e','l','l','o']
// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']

上面代码的扩展运算符内部就调用 Iterator 接口。

实际上,这提供了一种简便机制,可以将任何部署了 Iterator 接口的数据结构,转为数组。也就是说,只要某个数据结构部署了 Iterator 接口,就可以对它使用扩展运算符,将其转为数组。

let arr = [...iterable];

(3)yield*

yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。

let generator = function* () {
yield 1;
yield* [2,3,4];
yield 5;
};
var iterator = generator();
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }

(4)其他场景

由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。

  • for...of
  • Array.from()
  • Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]])
  • Promise.all()
  • Promise.race()

好了,今天就分享到这里了。如果有错误的地方或者问题,请各位大佬指正。如果有不清楚的可以留言。

最后

以上就是懦弱奇异果为你收集整理的解读 arrify 源码,Symbol.iterator的全部内容,希望文章能够帮你解决解读 arrify 源码,Symbol.iterator所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部