JavaScript迭代与迭代器(Iterable and Iterator)

2017-01-12 09:54:00来源:open-open作者:网络转载人点击

我们在很前面的时候就讲到了迭代器这么一个东西。那么他究竟是什么呢?又有什么样的作用呢?本节我们就来讲述 Iterables 与 Iterators。也就是可迭代性与迭代器。


概述

ES6 中新增了一个迭代的接口,叫做可迭代性(Iterable )。本节呢,将要向大家讲述它是怎么工作的,以及它运用于那些 ECMAScript 语言类型中。


我们来看看迭代(Iteration )是什么,它分为两个部分:

Iterable: 可迭代性是一种数据结构,它希望使其元素可以访问公共部分。它通过内置系统的一个方法,键为 Symbol.iterator。这个方法就是迭代器的工厂。
Iterator: 用于遍历数据结构的元素的指针。

以下的值具有可迭代性:

Array
String
Map
Set
DOM 数据结构(在程序中工作的那部分)

那么具有可迭代性的对象可以做哪些事呢?如下所示:


(1)解构

let [a,b] = new Set(['a', 'b']);
console.log(a);// "a"
console.log(b);// "b"

(2)for-of 循环

for (let x of ['a', 'b', 'c']) {
console.log(x);
}
// "a"
// "b"
// "c"

(3)Array.from( ) 方法

let arr = Array.from(new Set(['a', 'b', 'c']));
//["a", "b", "c"]

(4)展开操作符(...)

let arr = [...new Set(['a', 'b', 'c'])];
// ["a", "b", "c"]

(5)Map 和 Set 构造函数

let map = new Map([
[1,'a'],
[2,'b']
]);
let set = new Set(['a', 'b', 'c']);

(6)Promise.all( ) 、Promise.race( ) 方法

Promise.all(iterableOverPromises).then(···);
Promise.race(iterableOverPromises).then(···);

(7)yeild(*)

yield* anIterable;

看完以上的这些,记住了,基本上这节内容就大致清楚了。


可迭代性

这里我们将要讲到可迭代性的思想,也就是说,可迭代到底是什么?


可迭代性的思想:

数据消费者( Data consumers ):JavaScript 具有“消费”数据的语言结构。例如,for-of 循环用于遍历值,而扩展运算符(...)将值插入到数组或函数调用中。
数据源( Data Sources ):数据消费者可以从各种来源获取值。例如,您可能需要遍历数组的元素,或者Map 中的键值(使用 entries 方法)以及 String 实例中的字符等。

我们这里必须清楚,想要每个数据消费者都能获取数据源,这是不切实际的。尤其是当我们创建新的源时(例如通过一个库)。因此,ES6 就提供了 Iterable 的接口。数据消费者使用它,而数据源负责实现它。我们来看下图:



迭代关系


这里有两个知识:

源( Source ):如果一个键拥有 Symbol.Iterator 方法并返回迭代器时,它的值就被认为是可迭代的。迭代器是一个对象,它可以使用 next( ) 方法返回值。我们可以这样说:这个方法每次使用时,都可以枚举值。
消耗( Consumption ):数据使用者使用迭代器来检索它们消耗的值。

这里我们来举个之前的例子吧:

let set = new Set(['a','b','c']);
let keys = set.keys();
console.log(keys.next());// {value: "a", done: false}
console.log(keys.next());//{value: "b", done: false}
console.log(keys.next());// {value: "c", done: false}
console.log(keys.next());// {value: undefined, done: true}

上述示例中,我们使用了 keys() 方法与 next() 方法来进行该 Set 实例每个值的枚举与输出。就是因为 Set 拥有 Symbol.Iterator 方法。因此我们还可以使用下述的方式进行遍历:

let set = new Set(['a','b','c']);
let iter = set[Symbol.iterator]();
console.log(iter.next());// {value: "a", done: false}
console.log(iter.next());//{value: "b", done: false}
console.log(iter.next());// {value: "c", done: false}
console.log(iter.next());// {value: undefined, done: true}

输出结果一致,没有问题。


可以看到,next( ) 返回包装在对象中的每个项目,作为属性值的值。布尔属性 done 指示何时已达到项目序列的结束。


迭代和迭代器是所谓的协议(方法加上使用它们的规则)的一部分迭代。此协议的关键特性是它是顺序的:迭代器一次返回一个值。 这意味着如果一个可迭代的数据结构是非线性的(如一颗树),迭代将会使其线性化。


迭代的数据源

在概述中我举了几个例子。以下我使用 for-of 循环来迭代每个类型。我们一起来看下输出的值是什么:


(1)Array

for (let x of ['a', 'b']) {
console.log(x);
}
// "a"
// "b"

(2)String

for (let x of 'Hello') {
console.log(x);
}
// "H"
// "e"
// "l"
// "l"
// "e"

(3)Map

let map = new Map()
.set('a', 1)
.set('b', 2);
for (let pair of map) {
console.log(pair);
}
// ["a", 1]
// ["b", 2]

(4)Set

let set = new Set()
.add('a')
.add('b');
for (let x of set) {
console.log(x);
}
// "a"
// "b"

(5)arguments

function printArgs() {
for (let x of arguments) {
console.log(x);
}
}
printArgs('a', 'b');
// "a"
// "b"

(6)DOM 数据结构

for (let node of document.querySelectorAll('div')) {
// ···
}

现在更加清晰了吧?举出这么多例子的目的就是希望大家熟悉它。


迭代计算的数据

不是所有可迭代内容都必须来自数据结构,它也可以即时计算。


例如我们前面学习的 Array、Map、Set 都拥有 entries( )、keys( )、values( ) 三个方法。它们都是即时计算实例中的内容,再进行输出。

entries( ) 方法返回实例的 [key, value] 的数组。
keys( ) 方法返回实例的键。
values( ) 方法返回实例的值。

但是,我们需要知道,Object 类型是没有迭代性可言的。因此它没有 for-of 循环,只有 for-in 的遍历。当然,在未来可能会有内置方法。

// 错误
for (let x of {a: 1, b: 2}) { // TypeError
console.log(x);
}
总结

本节把迭代的知识点讲完了,但是具体的用法还是比较多的。需要我们不断地实践。当你忘记的时候,就回来看看,将这些可迭代的对象牢记在脑海中,在使用时,就不会出错。

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台