前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【ES6基础】迭代器(iterator)

【ES6基础】迭代器(iterator)

原创
作者头像
前端达人
修改2019-06-17 09:14:42
8460
修改2019-06-17 09:14:42
举报
文章被收录于专栏:前端达人

迭代器(iterator)是一个结构化的模式,用于从源以一次一个的方式提取数据。迭代器的使用可以极大地简化数据操作,于是ES6也向JS中添加了这个迭代器特性。新的数组方法和新的集合类型(如Set集合与Map集合)都依赖迭代器的实现,这个新特性对于高效的数据处理而言是不可或缺的,在语言的其他特性中也都有迭代器的身影:新的for-of循环、展开运算符(...),甚至连异步编程都可以使用迭代器。

今天笔者将从以下几个方面进行介绍迭代器:

  • 什么是迭代器(iterator)?
  • 基于协议实现迭代器
  • 迭代器的应用

本篇文章阅读时间预计6分钟。

迭代器(iterator)

迭代器是一种有序、连续的、基于拉取的用于消耗数据的组织方式,用于以一次一步的方式控制行为。简单的来说我们迭代循环一个可迭代对象,不是一次返回所有数据,而是调用相关方法分次进行返回。

迭代器iterator是一个object,这个object有一个next函数,该函数返回一个有value和done属性的object,其中value指向迭代序列中当前next函数定义的值。

ES6的迭代协议分为迭代器协议(iterator protocol)和可迭代协议(iterable protocol),迭代器基于这两个协议进行实现。

迭代器协议: iterator协议定义了产生value序列的一种标准方法。只要实现符合要求的next函数,该对象就是一个迭代器。相当遍历数据结构元素的指针,类似数据库中的游标。

可迭代协议: 一旦支持可迭代协议,意味着该对象可以用for-of来遍历,可以用来定义或者定制JS 对象的迭代行为。常见的内建类型比如Array & Map都是支持可迭代协议的。对象必须实现@@iterator方法,意味着对象必须有一个带有@@iterator key的可以通过常量Symbol.iterator访问到的属性。

下图展示了arrays,Maps,Strings数据类型实现了可迭代协议,我们可以使用for-of和展开语法显示迭代器的数据。

基于协议实现迭代器

迭代器协议

如下代码展示了基于迭代协议进行实现:

代码语言:javascript
复制
let obj = {
 array: [1, 2, 3, 4, 5],
 nextIndex: 0,
 next: function() {
         return this.nextIndex < this.array.length ? {value: this.array[this.nextIndex++], done: false} : {done: true}
       }
};
console.log(obj.next().value);
console.log(obj.next().value);
console.log(obj.next().value);
console.log(obj.next().value);
console.log(obj.next().value);
console.log(obj.next().done);

上述代码将会输出

代码语言:javascript
复制
1
2
3
4
5
true

上述代码的next方法还可以按如下代码进行改写,看起来更清晰些:

代码语言:javascript
复制
if(this.nextIndex < this.array.length) {
  this.nextIndex++;
  return { value: this.array[this.nextIndex], done: false }
} else {
  return { done: true }
}

我们可以看出,next方法的实现,如果存在新的元素,返回当前元素的并将当前元素位置的标识递增加1,当没有元素时,返回{ done: true }。

可迭代协议

根据可迭代协议,对象需要提供@@iterator方法; 也就是说,它必须将Symbol.iterator符号作为属性键。 @@iterator方法必须返回迭代器对象。代码实现如下:

代码语言:javascript
复制
let obj = {
  array: [1, 2, 3, 4, 5],
  nextIndex: 0,
  [Symbol.iterator]: function(){
    return {
     array: this.array,
     nextIndex: this.nextIndex,
     next: function(){
       return this.nextIndex < this.array.length ?
          {value: this.array[this.nextIndex++], done: false} :
          {done: true};
     }
    }
  }
};
let iterable = obj[Symbol.iterator]()
console.log(iterable.next().value);
console.log(iterable.next().value);
console.log(iterable.next().value);
console.log(iterable.next().value);
console.log(iterable.next().value);
console.log(iterable.next().done);

上述代码将会输出

代码语言:javascript
复制
1
2
3
4
5
true

上述代码,我们实现了自定义的迭代器,基于JS的作用域和闭包特性才能轻松实现。arrays,Maps,Strings数据类型实现了可迭代协议,其 __proto__原型链指向自带Symbol.iterator的方法,节省了我们手写代码的时间,如下代码所示:

代码语言:javascript
复制
const arr = [1, 2];
const iterator = arr[Symbol.iterator](); // returns you an iterator
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())

上述代码将会输出:

代码语言:javascript
复制
{ value: 1, done: false }
{ value: 2, done: false }
{ value: undefined, done: true }

我们可以使用for-of,展开语法迭代数组,示例代码如下:

代码语言:javascript
复制
const arr = [1, 2];
for(var v of arr){
    console.log(v);
}
//outputs 1
//outputs 2
console.log([...arr]);
//outputs[1,2];

obj对象没有实现可迭代协议,我们如何迭代obj对象呢?实现obj的迭代器呢,示例代码如下:

代码语言:javascript
复制
var obj={
    a:1,
    b:2,
    c:3,
    [Symbol.iterator]:function () {
        var keys=Object.keys(this);//object.vulues(this)
        var index=0;
        return{
            next:()=>
            (index<keys.length)?
            {value: this[keys[index++]], done:false} :{done: true,value:undefined}
        }
    }
};
console.log([...obj]);
//outputs [1,2,3]

迭代器应用

斐波那契数列

斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)。

我们可以使用迭代器来产生一个序列数不大于100的斐波那契数列,示例代码如下:

代码语言:javascript
复制
let Fib= {
    [Symbol.iterator](){
        let n1=1;
        let n2=1;
        let max=100;
        return {
            next(){
                let current=n2;
                n2=n1;
                n1=n1+current;
                if(current<max){
                    return {value: current, done: false}
                }else{
                    return { done: true}
                }

            }
        }
    }
}
console.log([...Fib]);
//outputs [ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ]

模拟任务队列

有时候我们需要要执行任务放在一个队列里,分次执行,我们可以使用迭代器进行模拟,示例代码如下:

代码语言:javascript
复制
let tasks={
    actions:[],
    [Symbol.iterator](){
        let 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}
                }
            },
            return(v){
                steps.length=0;
                return {value:v,done:true};
            }
        };
    }
};

tasks.actions.push(
    function step1(x) {
        console.log("step 1:",x);
        return x*2;
    },
    function step2(x,y) {
        console.log("step 2:",x,y);
        return x+(y*2);
    },
    function step3(x,y,z) {
        console.log("step 3:",x,y,z);
        return (x*y)+z;
    }
);

let it=tasks[Symbol.iterator]();
console.log(it.next(10));
console.log(it.next(20,50));
console.log(it.next(20,50,120));
console.log(it.next());

上述代码输出:

代码语言:javascript
复制
step 1: 10
{ value: 20, done: false }
step 2: 20 50
{ value: 120, done: false }
step 3: 20 50 120
{ value: 1120, done: false }
{ done: true }

从上述代码,我们可以看出,迭代器不仅仅是数据的迭代,还可以作为一个模式来组织相关的功能。(注:本示例来源《你不知道的JavaScript》下卷)

小节

今天的内容就到这里,迭代器是不是很神奇,好像如魔法一般,我们随意控制函数的中断与继续,丰富了我们解决问题的思路,让我们的代码看起来更加工程化和结构化,提高了代码的可读性和可理解性。

【ES6基础】let和作用域

【ES6基础】const介绍

【ES6基础】默认参数值

【ES6基础】展开语法(Spread syntax)

【ES6基础】解构赋值(destructuring assignment)

【ES6基础】箭头函数(Arrow functions)

【ES6基础】模板字符串(Template String)

【ES6基础】Set 与 WeakSet

【ES6基础】Map 与 WeakMap

【ES6基础】Symbol介绍:独一无二的值

【ES6基础】Object的新方法

【数据结构基础】栈简介(使用ES6)

【数据结构基础】队列简介(使用ES6)

更多精彩内容,请微信关注”前端达人”公众号!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 迭代器(iterator)
  • 基于协议实现迭代器
    • 迭代器协议
      • 可迭代协议
      • 迭代器应用
        • 斐波那契数列
          • 模拟任务队列
          • 小节
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档