时间:2021-05-28
一、什么是迭代器?
生成器概念在Java,Python等语言中都是具备的,ES6也添加到了JavaScript中。Iterator可以使我们不需要初始化集合,以及索引的变量,而是使用迭代器对象的 next 方法,返回集合的下一项的值,偏向程序化。
迭代器是带有特殊接口的对象。含有一个next()方法,调用返回一个包含两个属性的对象,分别是value和done,value表示当前位置的值,done表示是否迭代完,当为true的时候,调用next就无效了。
ES5中遍历集合通常都是 for循环,数组还有 forEach 方法,对象就是 for-in,ES6 中又添加了 Map 和 Set,而迭代器可以统一处理所有集合数据的方法。迭代器是一个接口,只要你这个数据结构暴露了一个iterator的接口,那就可以完成迭代。ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。
二、如何使用迭代器?
1、默认 Iterator 接口
数据结构只要部署了 Iterator 接口,我们就成这种数据结构为“可遍历”(Iterable)。ES6 规定,默认的 Iterator 接口部署在数据结构的 Symbol.iterator 属性,或者说,一个数据结构只要具有 Symbol.iterator 数据,就可以认为是“可遍历的”(iterable)。
可以供 for...of 消费的原生数据结构
可以看上面的原生数据结构中并没有对象(Object),为什么呢?
那是因为对象属性的遍历先后顺序是不确定的,需要开发者手动指定。本质上,遍历器是一种线性处理,对于任何非线性的数据结构,部署遍历器接口就等于部署一种线性变换。
做如下处理,可以使对象供 for...of 消费:
// code1function Obj(value) { this.value = value; this.next = null;}Obj.prototype[Symbol.iterator] = function() { var iterator = { next: next }; var current = this; function next() { if (current) { var value = current.value; current = current.next; return { done: false, value: value }; } else { return { done: true }; } } return iterator;}var one = new Obj(1);var two = new Obj(2);var three = new Obj(3);one.next = two;two.next = three;for (var i of one) { console.log(i);}// 1// 2// 32、调用 Iterator 接口的场合
(1) 解构赋值
// code2let set = new Set().add('a').add('b').add('c');let [x,y] = set;// x='a'; y='b'let [first, ...rest] = set;// first='a'; rest=['b','c'];(2) 扩展运算符
// code3// 例一var str = 'hello';[...str] // ['h','e','l','l','o']// 例二let arr = ['b', 'c'];['a', ...arr, 'd']// ['a', 'b', 'c', 'd'](3)Generator 函数中的 yield* 表达式(下一章介绍)
// code4let generator = function* () {yield 1;yield* [2,3,4];yield 5;};var iterator = generator();iterator.next() // { value: 1, done: false }iterator.next() // { value: 2, done: false }iterator.next() // { value: 3, done: false }iterator.next() // { value: 4, done: false }iterator.next() // { value: 5, done: false }iterator.next() // { value: undefined, done: true }(4)其它场合
3、for...of 循环的优势
先看看,数组 forEach 方法的缺点:
// code5myArray.forEach(function (value) { console.log(value);});这个写法的问题在于,无法中途跳出 forEach 循环,break 命令或 return 命令都不能生效。
再看看,对象 for...in 的循环的缺点:
for (var index in myArray) { console.log(myArray[index]);};那么,for...of 有哪些显著的优点呢?
4、各数据类型如何使用 for...of 循环?
(1)数组
for...of 循环允许遍历数组获得键值
for...of 循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的值
(2)Map 和 Set 结构
var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);for (var e of engines) { console.log(e);}// Gecko// Trident// Webkitvar es6 = new Map();es6.set("edition", 6);es6.set("committee", "TC39");es6.set("standard", "ECMA-262");for (var [name, value] of es6) { console.log(name + ": " + value);}// edition: 6// committee: TC39// standard: ECMA-262由上述的代码可以看出,for...of 循环遍历Map 和 Set 结构时,遍历的顺序是按照各个成员被添加进数据结构的顺序,Set 结构遍历时返回的是一个值,而 Map 结构遍历时返回的是一个数组,该数组的两个成员分别为当前 Map 成员的键名和键值。
(3)类数组对象
字符串
DOM NodeList 对象
arguments 对象
没有 Iterator 接口类数组对象的遍历处理
借用 Array.from 方法处理
(4)对象
对于普通对象,不能直接使用 for...of 遍历,否则会报错,必须部署了 Iterator 接口才能使用。如下两种方法部署:
// 方法一:使用 Object.keys 方法讲对象的键名生成一个数组for (var key of Object.keys(someObject)) { console.log(key + ": " + someObject[key]);}// 方法二:使用Generator 函数将对象重新包装一下function * entries(obj) { for (let key of Object.keys(obj)) { yield[key, obj[key]]; }}for (let[key, value] of entries(obj)) { console.log(key, "->", value);}// a -> 1// b -> 2// c -> 3三、迭代器应用实例
1、斐波那契数列
下面我们就使用迭代器来自定义自己的一个斐波那契数列组,我们直到斐波那契数列有两个运行前提,第一个前提是初始化的前两个数字为0,1,第二个前提是将来的每一个值都是前两个值的和。这样我们的目标就是每次都迭代输出一个新的值。
var it = { [Symbol.iterator]() { return this }, n1: 0, n2: 1, next() { let temp1 = this.n1, temp2 = this.n2; [this.n1, this.n2] = [temp2, temp1 + temp2] return { value: temp1, done: false } }}for (var i = 0; i < 20; i++) { console.log(it.next())}// "value": 0, "done": false} { "value": 1, "done": false} { "value": 1, "done": false} { "value": 2, "done": false} { "value": 3, "done": false} { "value": 5, "done": false}... { "value": 2584, "done": false} { "value": 4181, "done": false}2、任务队列迭代器
我们可以定义一个任务队列,该队列初始化时为空,我们将待处理的任务传递后,传入数据进行处理。这样第一次传递的数据只会被任务1处理,第二次传递的只会被任务2处理… 代码如下:
var Task = { actions: [], [Symbol.iterator]() { var steps = this.actions.slice(); return { [Symbol.iterator]() { return this; }, next(...args) { if (steps.length > 0) { let res = steps.shift()(...args); return { value: res, done: false } } else { return { done: true } } } } }}Task.actions.push(function task1(...args) { console.log("任务一:相乘") return args.reduce(function(x, y) { return x * y })},function task2(...args) { console.log("任务二:相加") return args.reduce(function(x, y) { return x + y }) * 2},function task3(...args) { console.log("任务三:相减") return args.reduce(function(x, y) { return x - y })});var it = Task[Symbol.iterator]();console.log(it.next(10, 100, 2));console.log(it.next(20, 50, 100)) console.log(it.next(10, 2, 1)) // 任务一:相乘 { "value": 2000, "done": false}任务二:相加 { "value": 340, "done": false}任务三:相减 { "value": 7, "done": false}3、延迟执行
假设我们有一个数据表,我们想按大小顺序依次的获取数据,但是我们又不想提前给他排序,有可能我们根本就不去使用它,所以我们可以在第一次使用的时候再排序,做到延迟执行代码:
var table = { "d": 1, "b": 4, "c": 12, "a": 12}table[Symbol.iterator] = function() { var _this = this; var keys = null; var index = 0; return { next: function() { if (keys === null) { keys = Object.keys(_this).sort(); } return { value: keys[index], done: index++>keys.length }; } }}for (var a of table) { console.log(a)} // a b c d四、结语
本章内容,重点是明白 Iterator 接口的机制,以及 for...of 循环的使用方法。下一章介绍生成器函数 Generator 函数。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
ES6新的数组方法、集合、for-of循环、展开运算符(...)甚至异步编程都依赖于迭代器(Iterator)实现。本文会详解ES6的迭代器与生成器,并进一步挖
本文实例讲述了ES6新特性之Iterator(遍历器)和for-of循环。分享给大家供大家参考,具体如下:1.遍历数组for-of工作原理:迭代器有一个next
本文主要介绍了关于ES6中Iterator与for...of循环的相关内容,分享出来供大家参考学习,需要的朋友们下面来一起看看详细的介绍:一、Iterator(
JavaScript中循环语句不少,for、forin、forof和forEach循环,今天对比Array、Object、Set(ES6)、Map(ES6)四种
使用ES6语法重构React组件在AirbnbReact/JSXStyleGuide中,推荐使用ES6语法来编写react组件。下面总结一下使用ES6class