探索ES6 Iterator(遍历器)
来源:互联网 发布:lol遇到一个网络问题 编辑:程序博客网 时间:2024/06/04 23:36
前言:
半年前快速过了一遍ES6语法,掌握并熟练了一些常用的ES6语法,比如:class、继承、模板字符串、解构赋值、导入导出、Promise等等,然而对于一些ES6的其他新特性,并没有认真研究,最近学习ES8时,学习了Object.entries方法,然后遇到了一个问题,这个问题是:为什么对象不可以用for of进行遍历?这才来研读了一下Iterator,很庆幸我在这里找到了答案,本文参考了阮一峰老师的ES6标准入门,为此买了一本纸质版的ES6标准入门以示感谢!
1、Iterator是什么?
我的理解就是为不同的数据结构统一遍历接口,以便按顺序进行遍历,通俗一点讲就是可以共用ES6的for of遍历机制
2、一起来看看ES6标准入门中关于Iterator的简单模拟实现
var it = makeIterator(['a', 'b']); it.next() // { value: "a", done: false }it.next() // { value: "b", done: false }it.next() // { value: undefined, done: true }function makeIterator(array) { //此方法会返回一个对象,改对象依赖于一个索引 var nextIndex = 0; //该索引的作用类似于指针 return { next: function() { return nextIndex < array.length ? {value: array[nextIndex++], done: false} : {value: undefined, done: true}; } };}
首先调用makeIterator方法得到一个遍历器对象,该对象里面有一个next属性对应一个方法,该方法用于进行遍历,该方法共享闭包作用域里面的nextIndex指针来进行遍历,每次执行next方法都会将nextIndex加1,指向数组的下一个成员,当指针nextIndex大于数组长度时,将返回{value: undefined, done: true},即完成状态。此处抛一个问题:for of遍历数据结构时是依据什么跳出遍历过程的?
3、Iterator接口部署在哪里
并不是所有的数据结构都部署了Iterator接口,只有部署了Iterator的数据结构才可以使用for of进行遍历,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性
console.log(Object.prototype[Symbol.iterator])//undefinedconsole.log(Array.prototype[Symbol.iterator])//function values() { [native code] }console.log(String.prototype[Symbol.iterator])//function [Symbol.iterator]() { [native code] }
这说明对象是没有部署Iterator接口的,而数组和字符串是部署了遍历借口的,下面我们来验证一下:
const obj={ "name":"luochao", "age" : "age"}for (const x of obj) { console.log(x); //obj[Symbol.iterator] is not a function}for (const y of ["1","2","3"]) { console.log(y); //1,2,3}for (const z of "lc") { console.log(z); //l,c}
当然你也可以这样遍历
let obj4= [6,7,8]let xx=obj4[Symbol.iterator](); //拿到遍历器对象for(let i=0;i<obj4.length;i++){ console.log(xx.next())}//Object {value: 6, done: false}//Object {value: 7, done: false}//Object {value: 8, done: false}console.log(xx.next())// {value: undefined,done: true}
此处抛出一个问题?你是否发现这两种遍历数组的方式差别在哪里了呢?你是否可以模拟的写出Array.prototype[Symbol.iterator]遍历接口的实现逻辑呢?聪明的你可能已经想出来了
原生部署了遍历接口的数据结构有:Array、Map、Set、String、TypedArray、函数的 arguments 对象
如果你没有写出来Array.prototype[Symbol.iterator]遍历接口的实现,也不要灰心,跟着我接着往下看
4、一个对象如果要具备可被for…of循环调用的 Iterator 接口,就必须在Symbol.iterator的属性上部署遍历器生成方法(原型链上的对象具有该方法也可),不理解不要紧,看一下ES6标准人门中的代码
class RangeIterator { constructor(start, stop) { this.value = start; this.stop = stop; } [Symbol.iterator]() { return this; } next() { var value = this.value; if (value < this.stop) { this.value++; return {done: false, value: value}; } return {done: true, value: undefined}; }}function range(start, stop) { return new RangeIterator(start, stop);}for (var value of range(0, 3)) { console.log(value); // 0, 1, 2}
这里我们看下使用typescript编译后的代码
var RangeIterator = (function () { function RangeIterator(start, stop) { this.value = start; this.stop = stop; } RangeIterator.prototype[Symbol.iterator] = function () { return this; }; RangeIterator.prototype.next = function () { var value = this.value; if (value < this.stop) { this.value++; return { done: false, value: value }; } return { done: true, value: undefined }; }; return RangeIterator;}());
通过编译后的代码可以发现遍历器接口被加在了RangeIterator.prototype[Symbol.iterator]上,发现了什么?for of遍历range(0, 3)生成的对象,直接拿到值,是不是可以看成range(0,3)生成的对象直接调用了原型RangeIterator.prototype上的[Symbol.iterator]方法拿到了遍历器对象,然后再调用了原型上的next方法拿到{ done: false, value: value }对象,然后在取对象的value属性拿到实际的value值:0,1,2,for of是不是真的做了这些事呢?
5、我们来重写一下Array.prototype[Symbol.iterator]函数(这只是我的假设,有不对的地方欢迎指正)
Array.prototype[Symbol.iterator]=function(){ this.nextIndex=0; this.next=function(){ return this.nextIndex < this.length ? {value: this[this.nextIndex++], done: false} : {value: 1, done: true}; } return this;}let arr3=[9,8,7];for(let attr of arr3){ console.log(attr) //9,8,7}let xx=arr3[Symbol.iterator](); //拿到遍历器对象for(let i=0;i<arr3.length;i++){ console.log(xx.next())}//Object {value: 9, done: false}//Object {value: 8, done: false}//Object {value: 7, done: false}console.log(xx.next())//Object {value: undefined, done: true}
应该是眼前一亮吧?我的设想是for of遍历具有iterator接口的数据结构,会先执行[Symbol.iterator]方法,然后返回遍历器对象(这里以arr3为例就是返回arr3),然后调用遍历器对象(arr3)的next方法拿到Object {value: 9, done: false},然后调用返回对象的value属性拿到具体的值
6、我们再来看一段代码验证我们的构想,并回答开始的一个问题:for of遍历数据结构时是依据什么跳出遍历过程的?
function 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, 3}
同样的我们来分析这段代码,for of循环执行,调用one对象(其实引用js高程上的话说应该叫指针)的[Symbol.iterator]方法拿到遍历器对象iterator,然后调用遍历器对象iterator的next方法,第一次执行current等于one指针指向的对象,所以会返回{ done: false, value: 1 },并且执行 current = current.next
后this就变成了two指针指向的对象,然后接下来遍历就应该遍历two指针指向的对象了,什么时候跳出,相信你也看出来了当current取three的next属性时,current为undefined,则返回{ done: true },当for of拿到{ done: true }将会跳出遍历,为了验证我的猜想,我把代码做了如下修改
function 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: false,value:"无限遍历"}; } } 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, 3}
当我把{done:true}修改为{ done: false,value:”无限遍历”}时,你讲会看到这个for of循环根本停不下来了,接下来控制台就是满屏的”无限循环”字眼,所以for of跳出循环是依据{done:true}进行跳出的
7、回到最开始的问题,为什么对象不可以用for of进行遍历?
最直接的回到应该是对象没有部署[Symbol.iterator]遍历接口,那它为什么不部署遍历接口呢?再次回到定义:部署遍历接口是为了数据结构的成员能够按某种次序排列,当你看了Array.prototype[Symbol.iterator]的实现
Array.prototype[Symbol.iterator]=function(){ this.nextIndex=0; this.next=function(){ return this.nextIndex < this.length ? {value: this[this.nextIndex++], done: false} : {value: 1, done: true}; } return this;}
你可能会注意到了this.nextIndex,value:this[this.nextIndex++],这充分表明部署遍历接口的数据接口都是有线性规律的,而对象的键值都是任意的,杂乱无章的,这就失去了部署遍历接口的意义
8、如何查看数据结构的遍历接口
数据结构之所以可以用for of进行遍历,是因为数据结构部署了遍历接口,而用for of进行循环遍历时,会默认调用待遍历数据的遍历接口,遍历接口一般是数据结构原型的某一个方法(比如:values,entries方法),下面我来教你如何查看数据结构的遍历接口:查看对象原型上的 Symbol.iterator
属性对应的方法
const set = new Set([ ['lc', 22], ['yx', 21]]);console.log(set);
set结构的 Symbol.iterator
属性对应的方法是values方法,这说明我们在调用for of对set结构进行遍历的时候,他相当于会调用values()方法拿到遍历器对象(其实 set[Symbol.iterator]
=== set.values
),也就是说你直接用for of遍历 set.values()
方法或者 set
是没有区别的
for (const v of set){ console.log(v); //["lc", 1],['yx', 21]}for (let v of set.values()){ console.log(v)//["lc", 1],['yx', 21]}console.log(set[Symbol.iterator]===set.values) //true
9、我有话说
let arr3=[9,8,7];let xx=arr3[Symbol.iterator](); //拿到遍历器对象console.log(xx) //[9, 8, 7, nextIndex: 0]let arr4=[9,8,7];let xxx=arr4[Symbol.iterator];console.log(xxx()) //为什么这里就不能拿到上面带nextIndex的数组了呢
欢迎大佬留言指正,万分感谢!!!
- 探索ES6 Iterator(遍历器)
- Iterator(遍历器) ES6
- ES6 Iterator遍历器
- ES6——Iterator(遍历器)
- 13、ES6 Iterator(遍历器)的概念
- ES6 -- 遍历器Iterator和for...of
- ES6(二) Iterator(遍历器)和for-of循环
- es6 iterator(十四)
- ES6-Iterator与for..of..遍历
- 【ES6学习】— (7)Set、Map数据结构与Iterator遍历器
- es6 itaertor遍历器
- ES6学习9(Iterator&for...of)
- ES6 —(Iterator 和 for...of)
- 【ES6】Iterator和Generator
- ES6学习笔记:Iterator
- Iterator遍历
- Iterator遍历
- Iterator遍历
- Spring源码(一) 源码转换eclipse工程
- 【Java Utility】Jsoup网页爬虫工具--设置Element的HTML内容【十二】
- Codeforces Round #422 A. I'm bored with life
- mysql学生选课
- 【JavaScript的条件操作符】
- 探索ES6 Iterator(遍历器)
- 栈的应用
- Struts framework的工作原理和组件
- 深度学习的最好方案,FPGA或GPU?
- 讲课大师 把微信消息同步转发到企业微信中
- Codeforces Round #422 B. Crossword solving
- Python:斐波那契数列
- 洛谷P1308 统计单词数
- Activity被意外销毁状态保持