理解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
}

原文地址:https://www.cnblogs.com/jyybeam/p/12019788.html

时间: 2024-08-24 20:10:33

理解ES6中的Iterator的相关文章

理解ES6中的Symbol

一.为什么ES6引入Symbol 有时候我们在项目开发的过程中可能会遇到这样的问题,我写了一个对象,而另外的同时则在这个对象里面添加了一个属性或是方法,倘若添加的这个属性或是方法是原本的对象中本来就有的,那么这个时候势必会造成冲突,那么为了防止这种冲突,ES6 就引入了Symbol 二.Symbol使用方法 Symbol是一个新的数据类型,所以不要因为Symbol的使用方法的特殊而认为它只是es6中的新的方法.它所代表的是独一无二的,即便其参数一致. 1.使用方法 Symbol的使用方法是Sym

ES6中的Iterator和for...of

先说说数组遍历的几种方法吧: 1,for循环: 缺点:写法麻烦2,内置forEach方法; 缺点:不能跳出循环;break和return都不能;3,for...in;    缺点:数组的键名是数字,但是for...in循环是以字符串作为键名"0"."1"."2"等等.for...in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键.某些情况下,for...in循环会以任意顺序遍历键名.总之,for...in循环主要是为遍历对象

ES6 中的 iterator

[简介] 遍历器/迭代器.任何数据结构只要部署 Iterator 接口,就可以完成遍历操作.这种数据结构是“可遍历的”(iterable). 如何判断是否可遍历? typeof target[Symbol.iterator] // function [作用] 1. 为各种数据结构,提供一个统一的.简便的访问接口: 2. 使得数据结构的成员能够按某种次序排列: 3. ES6 创造了一种新的遍历命令for...of 循环,Iterator 接口主要供for...of消费. [遍历] const co

理解es6中的const与“不变”

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动. 效果 对于简单类型的数据(数值.字符串.布尔值),值就保存在变量指向的那个内存地址,因此等同于常量. 对于复合类型的数据(主要是对象和数组),变量指向的内存地址,const只能保证这个指针是固定的,不能保证它指向的数据结构是不可变得 'use strict' const obj = {} const arr = [] obj.prop = 123 // 不改动指针 arr.push('Hello') // 只改

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

前面的话 用循环语句迭代数据时,必须要初始化一个变量来记录每一次迭代在数据集合中的位置,而在许多编程语言中,已经开始通过程序化的方式用迭代器对象返回迭代过程中集合的每一个元素 迭代器的使用可以极大地简化数据操作,于是ES6也向JS中添加了这个迭代器特性.新的数组方法和新的集合类型(如Set集合与Map集合)都依赖迭代器的实现,这个新特性对于高效的数据处理而言是不可或缺的,在语言的其他特性中也都有迭代器的身影:新的for-of循环.展开运算符(...),甚至连异步编程都可以使用迭代器 本文将详细介

使对象具有ES6中Iterator接口的实现方法

es6中只有具有iterator接口的数组或者类数组(arguments)都可以使用for of来循环,但是对于对象来说不行,可以利用迭代器中的原理来给对象生成一个迭代器,实现让对象可以使用for of 1 var j={ 2 name:'小红', 3 age:18 4 } 5 //给j对象添加一个iterator接口 6 j[Symbol.iterator]=function(){ 7 //使用object.keys()方法把j对象中的k值读取出来存在数组当中 8 var arr=Object

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

用循环语句迭代数据时,必须要初始化一个变量来记录每一次迭代在数据集合中的位置,而在许多编程语言中,已经开始通过程序化的方式用迭代器对象返回迭代过程中集合的每一个元素 迭代器的使用可以极大地简化数据操作,于是ES6也向JS中添加了这个迭代器特性.新的数组方法和新的集合类型(如Set集合与Map集合)都依赖迭代器的实现,这个新特性对于高效的数据处理而言是不可或缺的,在语言的其他特性中也都有迭代器的身影:新的for-of循环.展开运算符(...),甚至连异步编程都可以使用迭代器 一.引入 下面是一段标

深入理解ES6箭头函数中的this

简要介绍:箭头函数中的this,指向与一般function定义的函数不同,箭头函数this的定义:箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定. (1)一般函数this指向在执行是绑定  当运行obj.say()时候,this指向的是obj这个对象. var x=11; var obj={ x:22, say:function(){ console.log(this.x) } } obj.say(); //console.log输出的是22 (2)所谓的定义时候绑定,就

关于var和ES6中的let,const的理解

var的作用就不多说了,下面说说var的缺点: 1.var可以重复声明 var a = 1; var a = 5; console.log(a); //5 不会报错 在像这些这些严谨的语言来说,一般是不允许重复声明的. 2.无法限制修改 var a = 5; a = 6; console.log(a); //6 不会报错 3.没有块级作用域 if(true){ var a = 5; } console.log(a) // 5 以上几个就是我个人对var的看法 ES6中,多了两个声明let 和 c