js中的可迭代对象,Generator的迭代方式,yield *

可迭代对象

可迭代对象你可以理解为内部可循环遍历的对象。
在ES6中,可迭代有Array、Map、Set 还有 String;
当然还有一些特殊类型比如生成器(Generator)。

迭代方式

我就不带大家手写迭代器了,直接教大家应用。
下面以Generator为例向大家介绍两种迭代方式,第二种大家最熟悉。

1.Generator.next()

第一种是Generator 自带的迭代方式。
为什么我要从这个比较晦涩难懂的开始讲而不是先将for in , for of 呢?因为后面会让你们重新认识第一种。
首先我们创建一个Generator 函数。

function* func(){
    yield 1;
    yield 2;
    yield 3;
}

此时 func 是一个 生成器函数 [Generator Function] , 这里的 * 号虽然放在哪都没关系,这里的 * 与后面的 * 意义不同,这里声明的 [Generator Function] 不是一个可迭代对象,为了区分,我强烈建议大家放在funtion后,而不是函数名前。

我们执行该函数,会获得一个 Generator 对象,该对象是一个可迭代对象
这里一定要区分 [Generator Function] 和 [Generator] ,否则你后面会更加混淆!!!
究竟什么是可迭代对象????大家别着急,我会慢慢向你解释什么是可迭代对象。

我们先用Generator自带的.next()迭代该对象。

let _func = func();	//此时 _func 是一个可迭代对象[Generator]
_func.next();	//{value:1, done:false}
_func.next();	//{value:2, done:false}
_func.next().value;	//3

看不懂?没关系,我说过,迭代器有两种迭代方式,而Generator也是迭代器的一种。不过我还是强烈建议您补一下Generator的基础再来看我的文章,这篇文章的重点不是教你怎么用 Generator的。

上面的方法是一种迭代方式。


2.for in 、 for of

for in和 for of 是基于迭代器实现的;废话不多说,直接改写上面的例子

function* func(){	//func 是[Generator Function],不可迭代,别混淆!
    yield 1;
    yield 2;
    yield 3;
}

let _func = func();	// _func 是一个可迭代对象 [Generator]

for(let i of _func){
	console.log(i);	
}

// 1
// 2
// 3

Generator对象可以使用 for of 遍历,并且结果等价 Generator.next().value

我们尝试一下 for in :

for(let i in _func){
	console.log(i);	
}
//undefined

这里不用惊讶,因为能使用for in的往往都是具有内部索引的可迭代对象,比如 String 和 Array,我们尝试一下无内部索引的Set 对象。

let arr = [1,2,3];
let set = new Set(arr);
for(let i in arr){
	console.log(i)
}
// 0
// 1
// 2

for(let i in set){
	console.log(i)
}
//undefined

以上例子也说明Generator没有内部索引,因此大多数情况下我们使用 for of 能更方便遍历所有可迭代对象。


通过 yield * 迭代 可迭代对象

yield * 可以将一个可迭代对象进行迭代返回,这么说有点抽象,直接上例子。

function* func1(nums) {
  yield nums;
}

for (let item of func1([1, 2, 3])) {	//这里 func1([1,2,3]) 获取的就是一个 Generator对象,这是简写希望你能看懂
  console.log(item)
}
// [1, 2, 3]

可以看见,不使用yield * 来迭代 nums,那么获取到的就是整个nums数组对象。
如果我们使用了 yield *

function* func1(nums) {
  yield *nums;
}

for (let item of func1([1, 2, 3])) {	
  console.log(item)
}
// 1
// 2
// 3

这样,nums就会作为一个可迭代对象被迭代,然后返回其所有内部值。

下一个例子,我将引入其他可迭代对象。

let arr=[1,2,3];	
let set = new Set(arr);	// Set
let str = "abcd";	// string
let map = new Map([["name","dz"],["age","21"],["hobby","music"]]);
let obj = {name:"dz",age:"21",hobby:"music"};	//Object类型是不可迭代对象。 Uncaught TypeError: obj is not iterable

function* func1(nums) {
  yield *set;
  yield *str;
  yield *map;
  yield *obj
}

for (let item of func1()) {	
  console.log(item)
}
// 1
// 2
// 3
// a
// b
// c
// d
// ["name", "dz"]
// ["age", "21"]
// ["hobby", "music"]
// Uncaught TypeError: xxx..

可以看见,只要能迭代的类型,yield * 都能正常迭代返回。
可以通过for of 来判断一个类型是否能够迭代,比如对象类型就不能使用for of迭代。
当然别忘了,Generator 本身也是一个可迭代对象!!!

我将举一个特别典型的例子来帮你更加理解可迭代对象。

这个例子如果懂了,我觉得你几乎吃透了这个知识点,不说了开始套娃。

function* func1(...nums) {
  yield "--func1--";
  yield* nums;
  yield* func2(101, 102);	//func2() 返回的Generator 也可以迭代
  yield "--end--";
}

function* func2(...nums) {
  yield "--fun2--";
  yield nums;
  yield* nums;
  yield*"对不起";
  yield* func3("邓洲", "呀")
}

function* func3() {
  yield "--func3--";
  yield* arguments; //arguments也是可迭代对象
  yield* funcNoStar(arguments); //函数返回值是可迭代对象也可以
}

function funcNoStar(arr) {
  let res = ["--funcNoStar--"];
  for (let a of arr) {
    res.push(...a)
  }
  return res;
}

for (let item of func1(1, 2, 3)) {
  console.log(item)
}

最后结果
在这里插入图片描述


版权声明:本文为weixin_51116314原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。