简介

为了方便集合数据的遍历,在ES6中引入了一个iteration的概念。为我们提供了更加方便的数据遍历的手段。

一起来学习一下吧。

什么是iteration

iteration也称为遍历,就是像数据库的游标一样,一步一步的遍历集合或者对象的数据。

根据ES6的定义,iteration主要由三部分组成:

  1. Iterable

先看下Iterable的定义:

interface Iterable {
[Symbol.iterator]() : Iterator;
}

Iterable表示这个对象里面有可遍历的数据,并且需要实现一个可以生成Iterator的工厂方法。

  1. Iterator
interface Iterator {
next() : IteratorResult;
}

可以从Iterable中构建Iterator。Iterator是一个类似游标的概念,可以通过next访问到IteratorResult。

  1. IteratorResult

IteratorResult是每次调用next方法得到的数据。

interface IteratorResult {
value: any;
done: boolean;
}

IteratorResult中除了有一个value值表示要获取到的数据之外,还有一个done,表示是否遍历完成。

Iterable是一个接口,通过这个接口,我们可以连接数据提供者和数据消费者。

Iterable对象叫做数据提供者。对于数据消费者来说,除了可以调用next方法来获取数据之外,还可以使用for-of 或者 ...扩展运算符来进行遍历。

for-of的例子:

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

...扩展运算符的例子:

 const arr = [...new Set(['a', 'b', 'c'])];

Iterable对象

ES6中,可以被称为Iterable对象的有下面几种:

  • Arrays
  • Strings
  • Maps
  • Sets
  • DOM

先看一个Arrays的情况,假如我们有一个Arrays,可以通过Symbol.iterator这个key来获取到Iterator:


> const arr = ['a', 'b', 'c'];
> const 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 }

更加简单的办法就是使用for-of:

for (const x of ['a', 'b']) {
console.log(x);
}
// Output:
// 'a'
// 'b'

看一个遍历String的情况,String的遍历是通过Unicode code points来区分的:

for (const x of 'a\uD83D\uDC0A') {
console.log(x);
}
// Output:
// 'a'
// '\uD83D\uDC0A' (crocodile emoji)

上面的例子中,基础类型的String在遍历的时候,会自动转换成为String对象。

Maps是通过遍历entries来实现的:

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

还记得之前提到的WeakMaps吗?

WeakMap,WeakSet和Map于Set的区别在于,WeakMap的key只能是Object对象,不能是基本类型。

为什么会有WeakMap呢?

对于JS中的Map来说,通常需要维护两个数组,第一个数组中存储key,第二个数组中存储value。每次添加和删除item的时候,都需要同时操作两个数组。

这种实现有两个缺点,第一个缺点是每次查找的时候都需要遍历key的数组,然后找到对应的index,再通过index来从第二个数组中查找value。

第二个缺点就是key和value是强绑定的,即使key不再被使用了,也不会被垃圾回收。

所以引入了WeakMap的概念,在WeakMap中,key和value没有这样的强绑定关系,key如果不再被使用的话,可以被垃圾回收器回收。

因为引用关系是weak的,所以weakMap不支持key的遍历,如果你想遍历key的话,请使用Map。

看下Set的遍历:

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

我们还可以遍历arguments对象:

function printArgs() {
for (const x of arguments) {
console.log(x);
}
}
printArgs('a', 'b'); // Output:
// 'a'
// 'b'

对于大部分DOM来说,也是可以遍历的:

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

普通对象不是可遍历的

简单对象就是通过字面量创建出来的对象,这些对象虽然也有key-value的内容,但是是不可遍历的。

为什么呢?

因为可遍历对象比如Array,Map,Set也是普通对象的一种特例。如果普通对象可以遍历了,那么会导致可以遍历对象的一些遍历中的冲突。

for (const x of {}) { // TypeError
console.log(x);
}

虽然不能直接遍历普通对象,但是我们可以通过使用objectEntries方法来遍历普通对象。

先看下objectEntries的实现:

function objectEntries(obj) {
let iter = Reflect.ownKeys(obj)[Symbol.iterator](); return {
[Symbol.iterator]() {
return this;
},
next() {
let { done, value: key } = iter.next();
if (done) {
return { done: true };
}
return { value: [key, obj[key]] };
}
};
}

我们通过Reflect.ownKeys()反射拿到对象中的iterator.然后通过这个iterator来进行普通对象的遍历。

看下具体的使用:


const obj = { first: 'Jane', last: 'Doe' }; for (const [key,value] of objectEntries(obj)) {
console.log(`${key}: ${value}`);
} // Output:
// first: Jane
// last: Doe

自定义iterables

除了ES6中默认的iterables之外,我们还可以自定义iterables。

因为iterables是一个接口,我们只需要实现它就可以了。我们看一个iterables的例子:

function iterateOver(...args) {
let index = 0;
const iterable = {
[Symbol.iterator]() {
const iterator = {
next() {
if (index < args.length) {
return { value: args[index++] };
} else {
return { done: true };
}
}
};
return iterator;
}
}
return iterable;
}

iterateOver方法会返回一个iterable对象。在这个对象中,我们实现了Symbol.iterator为key的方法。这个方法返回一个iterator,在iterator中,我们实现了next方法。

上面的方法使用起来是下面的效果:

// Using `iterateOver()`:
for (const x of iterateOver('fee', 'fi', 'fo', 'fum')) {
console.log(x);
} // Output:
// fee
// fi
// fo
// fum

上面的例子中,如果Symbol.iterator返回的对象是iterable本身,那么iterable也是一个iterator。

function iterateOver(...args) {
let index = 0;
const iterable = {
[Symbol.iterator]() {
return this;
},
next() {
if (index < args.length) {
return { value: args[index++] };
} else {
return { done: true };
}
},
};
return iterable;
}

这样做的好处就是,我们可以使用for-of同时遍历iterables和iterators,如下所示:

const arr = ['a', 'b'];
const iterator = arr[Symbol.iterator](); for (const x of iterator) {
console.log(x); // a
break;
} // Continue with same iterator:
for (const x of iterator) {
console.log(x); // b
}

关闭iterators

如果我们需要遍历的过程中,从iterators中返回该怎么处理呢?

通过实现return方法,我们可以在程序中断的时候(break,return,throw)调用iterators的return。

function createIterable() {
let done = false;
const iterable = {
[Symbol.iterator]() {
return this;
},
next() {
if (!done) {
done = true;
return { done: false, value: 'a' };
} else {
return { done: true, value: undefined };
}
},
return() {
console.log('return() was called!');
},
};
return iterable;
}
for (const x of createIterable()) {
console.log(x);
break;
}
// Output:
// a
// return() was called!

上面例子中,我们通过break来中断遍历,最终导致return方法的调用。

注意,return方法必须要返回一个对象,{ done: true, value: x }

总结

上面就是ES6中引入的Iterables和iterators的一些概念。

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/es6-iterables-iterator/

本文来源:flydean的博客

欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

ES6中的新特性:Iterables和iterators的更多相关文章

  1. ES6中常用新特性讲解

    1.不一样的变量声明:const和let ES6推荐使用let声明局部变量,相比之前的var(无论声明在何处,都会被视为声明在函数的最顶部) let和var声明的区别: var x = '全局变量'; ...

  2. ES6中的新特性

    本人最近学习es6一些方法,难免有些手痒,想着能不能将这些方法总结下,如下 1.数组的扩展 1)首先什么是伪数组 无法直接调用数组方法或期望length属性有什么特殊的行为,但仍可以对真正数组遍历方法 ...

  3. ASP.NET 5与MVC 6中的新特性

    差点忘了提一句,MVC 6中默认的渲染引擎Razor也将得到更新,以支持C# 6中的新语法.而Razor中的新特性还不只这一点. 在某些情况下,直接在Web页面中嵌入某些JSON数据的方式可能比向服务 ...

  4. Webpack 3 中的新特性

    本文简短地分享下最新发布的 Webpack 3 中的新特性,供大家参考. 1. Webpack 3 的新特性 6 月 20 日,Webpack 发布了最新的 3.0 版本,并在 Medium 发布了公 ...

  5. 使用示例带你提前了解 Java 9 中的新特性

    使用示例带你提前了解 Java 9 中的新特性 转载来源:https://juejin.im/post/58c5e402128fe100603cc194 英文出处:https://www.journa ...

  6. HTML 5中的新特性

    HTML 5中的新特性 html5新增了一些语义化更好的标签元素.首先,让我们来了解一下HTML语义化. 1.什么是HTML语义化? 根据内容的结构化(内容语义化),选择合适的标签(代码语义化)便于开 ...

  7. (数据科学学习手札73)盘点pandas 1.0.0中的新特性

    本文对应脚本及数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 毫无疑问pandas已经成为基于Pytho ...

  8. 1 PHP 5.3中的新特性

    1 PHP 5.3中的新特性 1.1 支持命名空间 (Namespace) 毫无疑问,命名空间是PHP5.3所带来的最重要的新特性. 在PHP5.3中,则只需要指定不同的命名空间即可,命名空间的分隔符 ...

  9. ES6 主要的新特性

    本文基于lukehoban/es6features ,同时参考了大量博客资料,具体见文末引用. ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代号harmony( ...

随机推荐

  1. canvas绘制折线路径动画

    最近有读者加我微信咨询这个问题: 其中的效果是一个折线路径动画效果,如下图所示: 要实现以上路径动画,一般可以使用svg的动画功能.或者使用canvas绘制,结合路径数学计算来实现. 如果用canva ...

  2. Nacos使用 MySQL 8.0 提示Public Key Retrieval is not allowed

    原因如下(参考官网给出的连接选项): 如果用户使用了 sha256_password 认证,密码在传输过程中必须使用 TLS 协议保护,但是如果 RSA 公钥不可用,可以使用服务器提供的公钥:可以在连 ...

  3. Failed opening required

    报错 点击页面右下角的图标,再点击错误可以显示报错.或者在项目中runtime--log也可以查看error [64]think\\__require_file(): Failed opening r ...

  4. 三、jmeter常用的元件及组件

    一.HTTP cookie Manager 用来储浏览器产生的用户信息,Stepping Thread Group 可用于模拟阶梯加压! 二.HTTP Cache Manager 缓存管理器(模拟浏览 ...

  5. python主线程捕获子线程异常

    python内置threading.Thread类创建的子线程抛出的异常无法在主线程捕获,可以对该类进行优化,为子线程添加exit code属性,主线程通过获取子线程的返回状态,来判断子线程中是否发生 ...

  6. MySQL DDL详情揭露

    前言: MySQL中DDL语句,即数据定义语言,用于创建.删除.修改.库或表结构,对数据库或表的结构操作.常见的有create,alter,drop等.这类语句通常会耗费很大代价,特别是对于大表做表结 ...

  7. 2020 ICPC EC Final西安现场赛游记

    也不知道从何说起,也不知道会说些什么,最想表达的就是很累很累. 从第一天去的时候满怀希望,没什么感觉甚至还有一些兴奋.到后来一直在赶路,感觉很疲惫,热身赛的时候觉得马马虎虎,导致热身赛被咕.然后教练就 ...

  8. C++ primer plus读书笔记——第4章 复合类型

    第4章 复合类型 1. 如果将sizeof运算符用于数组名,得到的将是整个数组中的字节数. 2. 如果对数组的一部分进行初始化,则编译器把其他元素设置为0.因此,将数组中的所有元素初始化为0,只要显式 ...

  9. Django(27)类视图

    类视图 在写视图的时候,Django除了使用函数作为视图,也可以使用类作为视图.使用类视图可以使用类的一些特性,比如继承等. View django.views.generic.base.View是主 ...

  10. 5.配置IP

    静态IP配置 1.NAT模式设置 首先设置虚拟机中NAT模式的选项,打开VMware,点击"编辑"下的"虚拟网络编辑器",设置NAT参数 注意: VMware ...