理解ES6中的Iterator
一、为什么使用Iterator
我们知道,在ES6中新增了很多的特性,包括Map,Set等新的数据结构,算上数组和对象已经有四种数据集合了,就像数组可以使用forEach,对象可以使用for...in.. 进行遍历一样,是不是随着Map和Set的出现也应该给这两种集合一个遍历方法呢?如果这样的话js的方法对象就太多了,既然这四种集合都是需要遍历的,那么完全可以用一个统一的访问机制。于是乎Iterator应运而生。
二、Iterator是什么
Iterator是一个访问机制,同时是一个接口,可以实现在不同的数据集合中,完成它的功能。
Iterator本质是一个指针对象,其中包含一个next方法,这个方法可以改变指针的指向,并且返回一个包含value和done的对象,value为当前所指向的成员的值,done表示是否遍历完成。通过这些描述,我们大致可以手写一个Iterator,用于遍历数组:
- // 定义遍历器对象
- let Iterator = function (arr) {
- let index = 0; // 当前指针
- return {
- next() {
- return index < arr.length ? {value: arr[index++],done: false} : {value: undefined, done: true};
- }
- }
- };
- let arr = [1, 2, 3];
- let it = Iterator(arr);
- console.log(it.next()); // { value: 1, done: false }
- console.log(it.next()); // { value: 2, done: false }
- console.log(it.next()); // { value: 3, done: false }
- console.log(it.next()); // { value: undefined, done: true }
三、ES6中的Iterator接口的实现
我们知道Iterator的接口就是提供了一个统一的访问机制,如果我们像上面那样,不断的调用next()才能遍历完成,如果Iterator像java那样提供一个hasNext()方法的话,那么我们可以通过while进行遍历,事实上js中是没有的。之所以没有是因为ES6使用for...of...实现了对具有Symbol.iterator(可遍历)的数据结构的遍历,也就是说只要是包含Symbol.iterator属性的结构都可以使用for...of...进行遍历。接下来我们看看我们直接输出上面四种结构的Symbol.iterator属性是什么样子。
- let a = [1,2,3];
- let it_arr = a[Symbol.iterator]();
- it_arr.next(); // { value: 1, done: false }
- it_arr.next(); // { value: 2, done: false }
- it_arr.next(); // { value: 3, done: false }
- it_arr.next(); // { value: undefined, done: true }
- let b = new Set(["a","b","c"]);
- let it_set = b[Symbol.iterator]();
- it_set.next(); // { value: 1, done: false }
- it_set.next(); // { value: 2, done: false }
- it_set.next(); // { value: 3, done: false }
- it_set.next(); // { value: undefined, done: true }
- let c = new Map([["a","1"]]);
- let it_map =c[Symbol.iterator]();
- it_map.next(); // { value: [ 'a', '1' ], done: false }
- it_map.next(); // { value: undefined, done: true }
- let d = new Object();
- d.name = "Jyy";
- console.log(d[Symbol.iterator]()); // TypeError: d[Symbol.iterator] is not a function
上面是ES6中在四种数据集合中的Iterator的实现。可以看到ES6并没有在对象中原生部署Symbol.iterator属性,因此我们需要手动在对象中设置遍历器属性。下面就是一个简单的在一个对象中设置Symbol.iterator属性的例子。
- function Demo(list){
- this.list = list;
- this[Symbol.iterator] = function(){
- let index = 0;
- return {
- next: function(){
- return index < list.length ? {value: list[index++], done: false} : {value: undefined, done: true};
- }
- }
- }
- }
- let demo = new Demo([1,2,3,4]);
- for(let i of demo){
- console.log(i); // 1 2 3 4
- }
上面的代码就是手动在Demo对象中添加了一个遍历器对象,在next中实现遍历的逻辑,然后就可以使用for...of...进行遍历。当然我们也可以将遍历器对象部署在Demo的原型对象,for...of...依然会自动调用。
另外,对于类数组的对象(key值为数字索引,包含lenth属性),我们可以直接将数组的遍历器对象赋值给对象,代码如下:
- let obj = {
- 0 : "a",
- 1 : "b",
- 2 : "c",
- length :3,
- }
- obj[Symbol.iterator] = Array.prototype[Symbol.iterator];
- for(let i of obj){
- console.log(i); // a b c
- }
除此之外,String,函数的arguments对象都原生部署了遍历器接口
- // 字符串可用for...of...进行遍历
- let str = "123";
- for(let s of str){
- console.log(s); // 1 2 3
- }
- // 函数中的arguments对象可以使用for...of...进行遍历
- function demo(){
- for(let i of arguments){
- console.log(i); // 1,2,3
- }
- }
- demo(1,2,3)
四、使用Iterator接口的场合
除了for...of...,es6中还有哪些语法用到了iterator接口呢?
1. 解构赋值
- function Demo(list){
- this.list = list;
- this[Symbol.iterator] = function(){
- let index = 0;
- return {
- next: function(){
- index++;
- return index < list.length ? {value: list[index++], done: false} : {value: undefined, done: true};
- }
- }
- }
- }
- [a,...b] = new Demo([1,2,3,4]);
- console.log(b); // [2,3,4]
如上代码中每次返回的value都是1,我们在中括号(注意不是花括号)中使用结构赋值,发现b的值为[2,3,4]
2.扩展运算符
与解构赋值相反的扩展运算符,也使用了遍历器。
- function Demo(list){
- this.list = list;
- this[Symbol.iterator] = function(){
- let index = 0;
- return {
- next: function(){
- return index < list.length ? {value: list[index++], done: false} : {value: undefined, done: true};
- }
- }
- }
- }
- let demo = new Demo([1,2,3,4]);
- console.log([0, ...demo, 5]); //[ 0, 1, 2, 3, 4, 5 ]
五、与其他遍历语法的比较
在js中的遍历语法有:for循环,for...in..., forEach
1. for循环的缺点我们都知道,就是书写起来太麻烦,如果有快捷键的方式的话还好,如果没有,就要写很多的代码,而且看起来也不爽。
2. forEach书写起来比较简单,但是它的问题是无法中途跳出循环,break、return、continue等都不能起作用
3. for...in...是专门为遍历对象而设计的,如果使用这种方法遍历数组,key则会变为字符串,如下:
- let arr = [1,2,3];
- for(let a in arr){
- console.log(typeof a); // string
- }
另外我们可能在开发中遇到的问题就是,使用for...in...的话,那么会遍历原型链上的键
- let person = {
- age :25
- }
- person.prototype={
- name : "jyy"
- }
- for(let i in Person){
- console.log(i); // age prototype
- }
理解ES6中的Iterator的更多相关文章
- 前端知识体系:JavaScript基础-原型和原型链-理解 es6 中class构造以及继承的底层实现原理
理解 es6 中class构造以及继承的底层实现原理 原文链接:https://blog.csdn.net/qq_34149805/article/details/86105123 1.ES6 cla ...
- 【JS】325- 深度理解ES6中的解构赋值
点击上方"前端自习课"关注,学习起来~ 对象和数组时 Javascript 中最常用的两种数据结构,由于 JSON 数据格式的普及,二者已经成为 Javascript 语言中特别重 ...
- 理解ES6中的Symbol
一.为什么ES6引入Symbol 有时候我们在项目开发的过程中可能会遇到这样的问题,我写了一个对象,而另外的同时则在这个对象里面添加了一个属性或是方法,倘若添加的这个属性或是方法是原本的对象中本来就有 ...
- 深入理解 ES6中的 Reflect
阅读目录 一:Reflect.get(target, name, receiver) 二:Reflect.set(target,name,value,receiver) 三:Reflect.apply ...
- ES6 中的 iterator
[简介] 遍历器/迭代器.任何数据结构只要部署 Iterator 接口,就可以完成遍历操作.这种数据结构是“可遍历的”(iterable). 如何判断是否可遍历? typeof target[Symb ...
- 理解ES6中的Promise
一.Promise的作用 在ajax请求数据的过程中,我们可以异步拿到我们想要的数据,然后在回调中做相应的数据处理. 这样做看上去并没有什么麻烦,但是如果这个时候,我们还需要做另外一个ajax请求,这 ...
- 理解es6 中 arrow function的this
箭头函数相当于定义时候,普通函数.bind(this)箭头函数根本没有自己的this,导致内部的this就是定义时候的外层代码块中的this.外层代码块中的this,则取决于执行时候环境上下文cont ...
- 浅析ES6中的iterator
1.iterator迭代器必须保证其遍历终止条件可控,否则会形成死循环demo: //会用到iterator接口的场合 //1.for...of循环 //2. ...解构表达式 const obj = ...
- 理解es6中的const与“不变”
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动. 效果 对于简单类型的数据(数值.字符串.布尔值),值就保存在变量指向的那个内存地址,因此等同于常量. 对于复合类型 ...
随机推荐
- kbd_mode - 显示或者设置键盘模式
总览 (SYNOPSIS) kbd_mode [ -auks ] 描述 (DESCRIPTION) 如果 没有 参数 kbd_mode 会 显示 当前 键盘 的 模式, 如果 有 参数, 它会把 键盘 ...
- SQL Server设置启动存储过程
--设置开关 启动程序自动运行存储过程必须启动该命令 sp_configure "show advanced options",1; go reconfigure; go --设置 ...
- usermod 修改用户信息
7.2 usermod 修改用户信息 1.命令功能 usermod 修改已存在的用户账号信息. 2.语法格式 usermod option login 参数选项说明 选项 选项说明 -c 修改用户pa ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(4)|借用Borrowing]
[易学易懂系列|rustlang语言|零基础|快速入门|(4)] Borrowing 继续讲讲另一个重要的概念:借用(borrowing), 什么是借用? 我们先来看前一文章([易学易懂系列|rust ...
- 【hiho1041】国庆出游 dfs+bitset
题目大意:给定一棵 N 个节点的有根树,1 号节点为根节点,现遍历整棵树,要求每条边仅被经过两次,问是否存在一种特定的遍历方式使得 dfs 序中节点的相对前后关系符合给定的顺序. 题解: 首先,由于要 ...
- POJ-3020-Antena Placement(最小路径覆盖)
链接: https://vjudge.net/problem/POJ-3020 题意: The Global Aerial Research Centre has been allotted the ...
- PL/SQL(Procedure Language & Structured Query Language)
目前的PL/SQL包括两部分,一部分是数据库引擎部分:另一部分是可嵌入到许多产品(如C语言,JAVA语言等)工具中的独立引擎.可以将这两部分称为:数据库PL/SQL和工具PL/SQL. PL/SQL中 ...
- css---一个大div中套左右两个div,如何让最高的把最低的撑开?且把父级撑开呢?
到最后实现了效果,但是在理论上感觉还是很牵强,如果哪位大神有方法,请评论指出哦 Html: css:
- CodeForces 1200E Compress Words
\(C_n^m\)的typora,点了一下启用源代码模式就把我已经写好的博客弄没了,就给我留个标题,自动保存也只给我保存了个标题--\(C_n^m\),wdnmd Time limit 1000 ms ...
- 【bzoj2763】[JLOI2011]飞行路线
*题目描述: Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司.该航空公司一共在n个城市设有业务,设这些城市分别标记为0到n-1,一共有m种航线,每种航线连接两个城市,并且航线有一 ...