循环语句的问题

var colors = ["red", "green", "blue"];
for (var i = 0, len = colors.length; i < len; i++) {
console.log(colors[i]);
}

标准的for循环代码。虽然也算是简单,但是当循环多层嵌套的时候,则需要跟踪多个循环变量,不小心错误使用了其它循环的跟踪变量,则会导致程序出错。

比如常见的外层for循环使用i作为循环的索引,内层的for循环也使用了同名变量i作为循环的索引,运行时就会导致代码报错。

迭代器的出现旨在消除这种复杂性并减少循环中的错误。

什么是迭代器

迭代器是一种特殊的对象,它具有一些专门为迭代过程设计的专有接口,所有的迭代器对象都有一个next()方法,每次调用都返回一个结果对象。

结果对象有两个属性:

  1. value 下一个将要返回的值
  2. done 一个布尔型的值

    当没有更多可返回数据时返回true。

如果在最后一个值返回后再调用next()方法,结果对象中的done为false;属性value则包含迭代器最终返回的值,这个返回值不是数据集合的一部分,它与函数的返回值类似,是函数调用过程中最后一次给调用者传递信息的方法,如果没有相关数据则返回undefined。

使用ES5的语法创建一个迭代器:

function createIterator(items) {
var i = 0;
return {
next: function() {
var done = (i >= items.length);
var value = !done ? items[i++] : undefined; return {
done: done,
value: value
};
}
};
} var iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // {done: false, value: 1}
console.log(iterator.next()); // {done: false, value: 2}
console.log(iterator.next()); // {done: false, value: 3}
console.log(iterator.next()); // {done: true, value: undefined}
// 之后所有的调用都返回相同的内容
console.log(iterator.next()); // {done: true, value: undefined}

ES6中引入了一个生成器对象,它可以让创建迭代器的过程变得简单。

什么是生成器

生成器是一种返回迭代器的函数,通过function关键字后的星号(*)来表示,函数中会用到新的关键字 yield。星号可以紧挨着function关键字,也可以在中间添加一个空格。

// 生成器
function *createIterator() {
yield 1;
yield 2;
yield 3;
} let iterator = createIterator(); console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3

每当执行完一条 yield 语句后函数就会自动停止执行。

使用 yield 关键字可以返回任何值或表达式。

// 生成器
function *createIterator(items) {
for(let i = 0; i < items.length; i++) {
yield items[i];
}
} let iterator = createIterator([1, 2,3]); console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: undefined, done: true}

生成器函数表达式

只需在function关键字和小括号中间添加一个星号即可。

let createIterator = function *(items) {
for(let i = 0; i < items.length; i++) {
yield items[i];
}
} let iterator = createIterator([1, 2,3]);

生成器对象的方法

let o = {
createIterator: function *(items) {
for(let i = 0; i < items.length; i++) {
yield items[i];
}
}
}; let iterator = o.createIterator([1, 2, 3]);

也可以使用简写方法创建生成器

let o = {
*createIterator(items) {
for(let i = 0; i < items.length; i++) {
yield items[i];
}
}
}; let iterator = o.createIterator([1, 2, 3]);

可迭代对象和 for-of 循环

可迭代对象具有 Symbol.iterator 属性,是一种与迭代器密切相关的对象。

Symbol.iterator 通过指定的函数可以返回一个作用于附属对象的迭代器。

在ES6中,所有集合对象(数组、Set集合及Map集合)和字符串都是可迭代对象,这些对象中都有默认的迭代器。

ES中新加入的特性 for-of 循环需要用到可迭代对象的这些功能。

let values = [1, 2, 3];

for (let num of values) {
console.log(num);
}

这段代码的输出

1
2
3

for-of 循环通过调用 values 数组的 Symbol.iterator 方法来获取迭代器;

随后迭代器的 next() 方法被多次调用,从其返回对象的 value 属性读取值并存储在变量 num 中;

当结果对象的 done 属性为 true 时退出循环,所有 num 不会被赋值为 undefined。

访问默认迭代器

可以通过 Symbol.iterator 来访问对象的默认迭代器。

let values = [1, 2, 3];
let iterator = values[Symbol.iterator](); console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: undefined, done: true}

使用 Symbol.iterator 来检测对象是否是可迭代对象:

function isIterable(object) {
return typeof object[Symbol.iterator] === "function";
} console.log(isIterable([1, 2, 3])); // true
console.log(isIterable("Hello")); // true
console.log(isIterable(new Map())); // true
console.log(isIterable(new Set())); // true
console.log(isIterable(new WeakMap())); // false
console.log(isIterable(new WeakSet())); // false

创建可迭代对象

默认情况下开发者定义的对象都是不可迭代的,但如果给 Symbol.iterator 属性添加一个生成器,则可以将其变为可迭代对象。

let collection = {
items: [],
*[Symbol.iterator]() {
for (let item of this.items) {
yield item;
}
}
}; collection.items.push(1);
collection.items.push(2);
collection.items.push(3); for (let x of collection) {
console.log(x);
}

这段代码输出以下内容:

1
2
3

内建迭代器

集合对象迭代器

ES6有三种类型的集合对象:数组、Map集合和Set集合,这3中对象都内建了以下三种迭代器:

  1. entries() 返回一个迭代器,其值为多个键值对;
  2. values() 返回一个迭代器,其值为集合的值(后面在运行示例时发现,数组没有该方法,但存在相同功能的迭代器);
  3. keys() 返回一个迭代器,其值为集合中的所有键名。

entries() 迭代器

返回一个数组,数组中两个元素分别表示为每个元素的键和值。

遍历对象是数组时,键是数字类型的索引;Set集合时,键同值一样;Map集合时,第一个元素为键名,第二个元素为值。

let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ECMAScript 6");
data.set("format", "ebook"); for (let entry of colors.entries()) {
console.log(entry);
} for (let entry of tracking.entries()) {
console.log(entry);
} for (let entry of data.entries()) {
console.log(entry);
}

上述代码执行结果如下:

[0, "red"]
[1, "green"]
[2, "blue"]
[1234, 1234]
[5678, 5678]
[9012, 9012]
["title", "Understanding ECMAScript 6"]
["format", "ebook"]

values() 迭代器

返回集合中所存的所有值。

let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ECMAScript 6");
data.set("format", "ebook"); for (let value of colors) {
console.log(value);
} for (let value of tracking.values()) {
console.log(value);
} for (let value of data.values()) {
console.log(value);
}

上述代码执行结果如下:

red
green
blue
1234
5678
9012
Understanding ECMAScript 6
ebook

关于数组的 values() 迭代器,书上示例的写法是 let value of colors.values(),但实际运行时出错了。

Uncaught TypeError: colors.values(...) is not iterable

但实际上该迭代器还是存在的,可以通过 colors[Symbol.iterator] 查看:

ƒ values() { [native code] }

因为数组的默认迭代器即为 values() 迭代器,所以可以省略 values(),直接使用变量本身 let value of colors 即可。

keys() 迭代器

返回集合中存在的每一个键。

数组类型返回数字类型的键;Set类型因为键和值相同,所有返回的也是值;Map类型返回的是所有的键。

let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ECMAScript 6");
data.set("format", "ebook"); for (let key of colors.keys()) {
console.log(key);
} for (let key of tracking.keys()) {
console.log(key);
} for (let key of data.keys()) {
console.log(key);
}

执行结果:

0
1
2
1234
5678
9012
title
format

不同集合类型的默认迭代器

每个集合都有一个默认的迭代器,在 for-of 循环中,如果没有显示指定迭代器,则使用默认的迭代器。

数组和Set集合的默认迭代器是 values() 方法;Map集合的默认迭代器是 entries() 方法。

let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ECMAScript 6");
data.set("format", "ebook"); for (let key of colors) {
console.log(key);
} for (let key of tracking) {
console.log(key);
} for (let key of data) {
console.log(key);
}

执行结果:

red
green
blue
1234
5678
9012
["title", "Understanding ECMAScript 6"]
["format", "ebook"]

字符串迭代器

【读书笔记】【深入理解ES6】#8-迭代器(Iterator)和生成器(Generator)的更多相关文章

  1. ES6中的迭代器(Iterator)和生成器(Generator)

    前面的话 用循环语句迭代数据时,必须要初始化一个变量来记录每一次迭代在数据集合中的位置,而在许多编程语言中,已经开始通过程序化的方式用迭代器对象返回迭代过程中集合的每一个元素 迭代器的使用可以极大地简 ...

  2. 迭代器 (Iterator) 和 生成器 (Generator)

    其他章节请看: es6 快速入门 系列 迭代器 (Iterator) 和 生成器 (Generator) 试图解决的问题 let colors = ['red', 'blue', 'green', ' ...

  3. ES6中的迭代器(Iterator)和生成器(Generator)(一)

    用循环语句迭代数据时,必须要初始化一个变量来记录每一次迭代在数据集合中的位置,而在许多编程语言中,已经开始通过程序化的方式用迭代器对象返回迭代过程中集合的每一个元素 迭代器的使用可以极大地简化数据操作 ...

  4. TypeScript 迭代器(iterator)和生成器(generator)

    ⒈迭代器(iterator) 1.可迭代性 当一个对象实现了Symbol.iterator属性时,我们认为它是可迭代的. 一些内置的类型如 Array,Map,Set,String,Int32Arra ...

  5. Python进阶内容(四)--- 迭代器(Iterator)与生成器(Generator)

    迭代器 我们已经知道,可以直接作用于for循环的数据类型有以下几种: 一类是集合数据类型,如list.tuple.dict.set.str等: 一类是generator,包括生成器和带yield的ge ...

  6. ES6中的迭代器(Iterator)和生成器(Generator)(二)

    一.内建迭代器 迭代器是ES6的一个重要组成部分,在ES6中,已经默认为许多内建类型提供了内建迭代器,只有当这些内建迭代器无法实现目标时才需要自己创建.通常来说当定义自己的对象和类时才会遇到这种情况, ...

  7. [TimLinux] Python 迭代器(iterator)和生成器(generator)

    1. 可迭代对象 from collection import Iterable class Iterable(metaclass=ABCMeta): ... def __iter__(self): ...

  8. 20150206读书笔记<深入理解计算机系统>

    ●第一章 C是系统级编程的首选.C++显示支持抽象,属于应用级程序设计语言. 简单例子: 一个典型系统的硬件组成: 存储器的层次结构: 注:存储器层次结构的设计思想是,该层存储器作为下一层存储器的高速 ...

  9. 深入理解ES6之迭代器与生成器

    迭代器 迭代器 iterator,在 Javascript 中,迭代器是一个对象(也可称作为迭代器对象),它提供了一个 next() 方法,用来返回迭代序列中的下一项. next 方法的定义,next ...

  10. 理解ES6中的Iterator

    一.为什么使用Iterator 我们知道,在ES6中新增了很多的特性,包括Map,Set等新的数据结构,算上数组和对象已经有四种数据集合了,就像数组可以使用forEach,对象可以使用for...in ...

随机推荐

  1. 机器学习——深度学习(Deep Learning)

    Deep Learning是机器学习中一个非常接近AI的领域,其动机在于建立.模拟人脑进行分析学习的神经网络,近期研究了机器学习中一些深度学习的相关知识,本文给出一些非常实用的资料和心得. Key W ...

  2. Java -Xms -Xmx -Xss -XX:MaxNewSize -XX:MaxPermSize含义记录

    出现java.lang.OutOfMemoryError异常时,常使用的方法便是将例如以下配置语句: -Xms512m -Xmx512m -Xss1024k -XX:MaxNewSize=256M - ...

  3. 输入url会发什什么

    从输入url到页面加载完成发生了什么 整体来说有几个基本的点: 1.浏览器的地址栏输入url并按下回车 2.浏览器查找当前url是否存在缓存,并比较缓存是否过期 3.DNS解析url对应的IP 4.根 ...

  4. IDEA+PHP+XDebug调试配置

    XDebug调试配置 临时需要调试服务器上的PHP web程序,因此安装xdebug,下面简单记录 安装xdebug 下载最新并解压 wget https://xdebug.org/files/xde ...

  5. django的CMS系统(内容管理系统)

    一.什么是CMS系统 CMS具有许多基于模板的优秀设计,可以减少开发的成本. CMS的功能并不只限于文本处理,它也可以处理图片.Flash动画.声像流.图像甚至电子邮件档案. CMS还分各个平台脚本种 ...

  6. svn协同开发下的dll版本管理最佳实践

    作为一名开发人员,常常碰到的一个问题是,当使用svn签出一份最新代码时,经常不能一次编译通过,导致花费大量时间去解决编译问题,这里碰到的问题一般可以分为三类: 1. 由于提交代码的开发人员失误,忘记提 ...

  7. 使用docker+consul+nginx集成分布式的服务发现与注册架构

    一.环境说明: 1.一台虚拟机,该系统已经装好了docker: ip 192.168.10.224 虚拟网卡,与主机互通 操作系统rhel6 内核 2.6.32  64位 docker版本 1.7.1 ...

  8. CoreJava逻辑思维-顺时针打印自定义矩阵

    CoreJava逻辑思维-顺时针打印自定义矩阵 这两天回顾了一下刚入Java时的一些比较有意思的逻辑题,曾经也费劲脑汁的思考过的一些问题,比如百钱百鸡最简单的算法啦之类的,而今天博主想说的是一个循环打 ...

  9. SDWebimage如何获取缓存大小以及清除缓存

    sdwebimage如何获取缓存大小以及清除缓存 1.找到SDImageCache类 2.添加如下方法: - (float)checkTmpSize {    float totalSize = 0; ...

  10. Asp.Net Web API中使用Session,Cache和Application的几个方法

    在ASP.NET中,Web Api的控制器类派生于ApiController,该类与ASP.NET的Control类没有直接关系,因此不能像在Web MVC中直接使用HttpContext,Cache ...