For of 是什么

ES6中引入的for of循环可以用来替代 for in 和 forEach(),并支持新的迭代协议,允许你遍历 Array、Maps(映射)、String、Sets(集合)、Arguments Object 等可迭代的数据结构等。

语法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
var arr = [1,2,3,4]

for(const value of arr){
    console.log(value);
}

//maps 
const iterable = new Map([['one', 1], ['two', 2]]);

for(const [ key, value ] of arr){
    console.log(` key:$[key] and value:$[value] `);
}

注:普通对象不可迭代

why???

首先我们想一个问题,为什么我们使用 for-of 或者 map()/ filer() 方法就可以遍历一个数组 (或者类数组对象: Strings , Maps , Sets , arguments ) 呢?

站在for-of的角度想一下,如果我们需要遍历一个数组,你需要知道什么信息呢?

  • 对应下标的值
  • 是否遍历结束的标志
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
//查看array对象

const newArr = [1,2,3]

console.dir(newArr);

//都存在一个Symbol.iterator

let iterator = nweArr[Symbol.iterator]();

//里面有个next方法

iterator.next();

//下标超出时 value: undefined
//iterator.next(); 每次都返回一个对象,包含两个信息
//对应下标的值 && 是否遍历结束

以上,印证了之前的猜想

So Iterator是什么

迭代器(iterator)与迭代协议(The iterator protocol)

迭代协议:

The iterator protocol 迭代协议允许 javascript对象去定义或者定制它们的迭代行为,所以上面出现的Symbol.iterator这个方法,就是数组对于这个协议的实现。那么按照这个协议,数组是如何实现了一个iterator呢?

当需要对一个对象进行迭代时(比如开始一个for of循环),它的@@iterator方法都会在不传参的情况下调用,返回的迭代器用于获取要迭代的值。

一些内置类型拥有默认的迭代器方法[Array,TypeArray,String,Map,Set],其他类型(Object)则没有。

可以自定义迭代器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var myIterable = {}
myIterable[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};
[...myIterable] // [1, 2, 3]

//如果一个迭代器没有返回一个迭代器对象,那么它就是一个不符合标准的迭代器
//运行时会抛出异常

为什么object中没有迭代器呢??

在很多场景中,我们也需要遍历一个对象,为什么不内置一个迭代器呢?

我们可以从另一个角度出发,了解一些基本的概念:

我们常说遍历对象,实在两个程度上对一个js对象进行遍历

  • 程序的层级:什么意思呢,在程序的层级上,我们对一个对象进行迭代,是在迭代展示其结构的对象属性。例如:array.prototype.length这个属性与对象的结构相关,但却不是它的数据。

  • 数据的层级:意味着迭代数据结构并提取它的数据。例如:我们在迭代一个数组的时候,迭代器是对于它的 每一个数据进行迭代,如array=[a,b,c,d]那么迭代器访问到的是,1,2,3,4。

js中提供了 for in 方法来遍历所有非Symbol但是可枚举的数据类型。

如果任性的就是要使用 for of来遍历,则可以使用自定义迭代器的方式呀。[乖巧]

补充:object 只是对象 万物皆对象,它是最底层的抽象。

可遍历的对象是 为 具有父子结构的数据而设计的,是比object更高一级的抽象,不适用于所有的object,因此object并没有预制迭代器。

以上。

Generators

1
function* () {};

在上文迭代器实现中出现过此规则函数。即Generator函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14

function* gen(){
    yield 1;
    yield 2;
    yield 3;
    yield 4;
}

//调用
let s = gen();

s.next();

consoel.dir(s);

由以上见,Generator 可以实例化出一个iterator,并且yield就是用来中断代码执行的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
function* gen() {
  yield 1;
  yield* gen2();
  
return;
}

function* gen2() {
  yield 4;
  yield 5;
}

let iterator = gen();
console.log(iterator.next);
console.log(iterator.next);
console.log(iterator.next);
console.log(iterator.next);

Generator 可以嵌套 Generator 并且 return 会终极整个Generator;

业务场景

两个异步函数 A、B , B的参数是A的返回值,也就是说A执行完之后才可以执行B ,由此可在A结束的时候调用 next;

其他:可参考 co库源码