资讯专栏INFORMATION COLUMN

ES6中的Iterator和for...of循环那些事

OpenDigg / 908人阅读

摘要:一个对象如果要有可被循环调用的接口,就必须在的属性上部署遍历器生成方法原型链上的对象具有该方法也可。阮大神案例上面代码是一个类部署接口的写法。属性对应一个函数,执行后返回当前对象的遍历器对象。

最近看阮一峰阮大神的ES6,刚刚看到Iterator和for...of循环这一章,小作笔记跟大家略微分享一下,不足之处还望大家多多指正

Iterator(遍历器)就是一种机制;任何数据结构只要是部署了iterator接口,就可以完成遍历操作(即依次处理该数据的所有成员);

Iterator的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。(阮大神原话);

只要是一个对象部署了Symbol.interator接口,就可以用for...of遍历该对象,同时也可以调用该接口的Symbol.interator方法调用next()方法对对象进行遍历,不同的是for..of是对该对象的值的输出,而next()返回的是对象。
例如下面的例子:


如果调用next方法就是

调用next()方法会返回一个对象{value:value;done:true or false};直到对遍历对象的值便利完成之后,next()方法会返回一个{value:undefined; done:true}代表已经遍历完成,再调用next()方法也不会报错但是依旧会返回{value:undefined; done:true};

有一些对象是原生就封装好的Symbol.iterator接口,可以直接调用,当然也可以直接用for...of遍历;
在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。
如果没有原生的Symbol.iterator接口,想用for...of遍历,就需要自己在该对象中部署Symbol.iterator接口,例如

一个对象如果要有可被for...of循环调用的Iterator接口,就必须在Symbol.iterator的属性上部署遍历器生成方法(原型链上的对象具有该方法也可)。

一个对象如果要有可被for...of循环调用的Iterator接口,就必须在Symbol.iterator的属性上部署遍历器生成方法(原型链上的对象具有该方法也可)。

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};
    } else {
      return {done: true, value: undefined};
    }
  }
}

function range(start, stop) {
  return new RangeIterator(start, stop);
}

for (var value of range(0, 3)) {
  console.log(value);
}//阮大神案例

上面代码是一个类部署Iterator接口的写法。Symbol.iterator属性对应一个函数,执行后返回当前对象的遍历器对象。
原型链上部署Symbol.iterator接口

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;
      var done = current == null;
      current = current.next;
      return {
        done: done,
        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

对象内部部署

let obj = {
  data: [ "hello", "world" ],
  [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index < self.data.length) {
          return {
            value: self.data[index++],
            done: false
          };
        } else {
          return { value: undefined, done: true };
        }
      }
    };
  }
};

也就是说如果一个对象没有Symbol.iterator接口,可以在类本身上面部署,也可以在原型连上部署,也可以在对象内部部署

对于类似数组的对象(存在数值键名和length属性),部署Iterator接口,有一个简便方法,就是Symbol.iterator方法直接引用数组的Iterator接口。

NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];

[...document.querySelectorAll("div")] // 可以执行了

具体请看例子

let iterable = {
  0: "a",
  1: "b",
  2: "c",
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); // "a", "b", "c"
}

**但是请记住,这个方法仅仅适用于类数组对象,上面的也可以直接用Array.from(iterable)转换成数组来遍历,例如

let arrayLike = { 
  length: 2, 
  0: "a", 
  1: "b"
 };

for (let x of Array.from(arrayLike)) {
  console.log(x);
}//a b

对于普通对象这两个方法是不管用的,**例如

let iterable = {
  edition: "a",
  writer: "b",
  read: "c",
  length: 3,
};
for (let item of Array.from(iterable)) {
  console.log(item); 
}

上面的代码就会输出三个undefined

let iterable = {
  edition: "a",
  writer: "b",
  read: "c",
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); //报错
}

上面的代码就会抛异常的;

其实ES6很多地方都用的到Iterator这个接口,例如:

解构赋值

扩展运算符

yield*

最后需要说下,字符串既是类数组对象同时自己也有原生Symbol.iterator接口可以直接调用,例如
var str = "hell";
for(let v of str){
console.log(v);//"h","e","l","l"
}
对于字符串来说,for...of循环还有一个特点,就是会正确识别32位UTF-16字符。

for...of 区别于for循环,for循环比较麻烦,但是是最原始的方法;
for...of 区别于数组的forEach方法,因为forEach方法是从头到尾执行,不会跳出,但是遇到break或者return,continue会跳出循环

有着同for...in一样的简洁语法,但是没有for...in那些缺点。
不同用于forEach方法,它可以与break、continue和return配合使用。
提供了遍历所有数据结构的统一操作接口。

更详细的请查看阮大神(http://es6.ruanyifeng.com/#docs/iterator)本章内容

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86086.html

相关文章

  • forEach到迭代器

    摘要:本文从使用对数组进行遍历开始说起,粗略对比使用进行遍历的差异,并由此引入中可迭代对象迭代器的概念,并对其进行粗略介绍。说到这里,就继续说一下迭代器关闭的情况了。确实,符合可迭代协议和迭代器协议的。 本文从使用 forEach 对数组进行遍历开始说起,粗略对比使用 forEach , for...in , for...of 进行遍历的差异,并由此引入 ES6 中 可迭代对象/迭代器 的概...

    rockswang 评论0 收藏0
  • js遍历之for forEach in of

    摘要:语法参数当前遍历项。遍历的范围在第一次调用前就会确定。已删除的项不会被遍历到。的是由提出的,目的是作为遍历所有数据结构的统一方法。不仅可以遍历数组,还可以遍历结构,某些类似数组的对象如对象对象,对象,以及字符串。 即使是最简单的循环,其中也深藏学问 ECMAScript5(es5)有三种for循环 简单for for in forEach ECMAScript6(es6)新增 fo...

    yeyan1996 评论0 收藏0
  • JavaScript:遍历

    摘要:中可以实现遍历的数据类型主要是对象,其中包括普通对象与数组。遍历器是一种接口,为各种不同的数据结构提供统一的访问机制。实例五遍历器对象实例五是的遍历过程,通过手动调用其对象的方法实现信息获取。为每个数组元素执行函数。 前言   将依据自身痛点学习,计划对原生JavaScript写一个系统,本文为第一篇,感兴趣的同学可以关注个人公众号:ZeroToOneMe,或者github博客,将持续...

    ThinkSNS 评论0 收藏0
  • ES6语法之可迭代协议迭代器协议

    摘要:有两个协议可迭代协议和迭代器协议。为了变成可迭代对象,一个对象必须实现或者它原型链的某个对象必须有一个名字是的属性迭代器协议该迭代器协议定义了一种标准的方式来产生一个有限或无限序列的值。 ECMAScript 2015的几个补充,并不是新的内置或语法,而是协议。这些协议可以被任何遵循某些约定的对象来实现。有两个协议:可迭代协议和迭代器协议。 可迭代协议 可迭代协议允许 JavaScri...

    sarva 评论0 收藏0
  • 从贺老微博引出的“遍历器(Iterators)加速那些奥秘”

    摘要:我关注的贺老贺师俊前辈最近发表个这样一条微博虽然这条微博没有引起大范围的关注和讨论,但是作为新人,我陷入了思考。通过贺老的微博,对一个问题进行探究,最终找到核心成员的一文,进行参考并翻译。 我关注的贺老—贺师俊前辈@johnhax 最近发表个这样一条微博: showImg(https://segmentfault.com/img/remote/1460000010452807); 虽然...

    XUI 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<