ES6 新特性梳理系列丨Iterator 和 for...of 循环

ES6 新特性梳理系列文章将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!


JavaScript 与 ECMAScript

JavaScript 诞生于1995年,设计者是就职于 Netscape 公司的工程师 Brendan Eich。它是一门仅用10天就完成设计的编程语言,但至今为止已对业界保持26年的影响,且势头愈发强劲

1996年 Netscape 将 JavaScript 提交给ECMA,希望它可以成为“标准化一个通用的、跨平台的、中立于厂商的脚本语言的语法和语义标准”,1997年 ECMA 确定将 JavaScript 作为浏览器脚本语言的标准,并为之重命名为 ECMAScript,所以通常来讲我们将 ECMAScript 视为 JavaScript 的标准,而 JavaScript 则是 ECMAScript 的实现及扩展

1997年-1999年连续发布了ES1-ES3发布,2000年开始酝酿新版本的升级内容,中间因标准委员会意见未能达成一致,只做了部分功能的小范围升级及支持,于2009年12月发布了过渡版 ECMAScript 5.0,2011年6月发布 ECMAScript 5.1 并成为 ISO 国际标准

2013年3月 ECMAScript 6 草案冻结,2013年12月 ECMAScript 草案发布,2015年6月 ECMAScript 6 正式通过,成为新的国际标准。ES6 泛指自2015年升级为 ECMAScript 6.0 后的所有子版本,如:ES2015-ES2020,等同于ES6.0-ES6.5,这是 JavaScript 走向企业级编程语言的强势升级

不断升级的标准与实现,对于开发效率及产品质量起到强有力的支撑,接下来我们开始梳理ES6的新特性吧!


什么是 iterator

iterator 是 ES6 新增的一种接口机制,为各种不同的数据类型提供一个统一的访问机制。任何数据类型只要拥有 iterator 接口,就可以完成遍历操作。比如 ES6 新增的 for...of 循环就是专门为 iterator 接口而生的。


iterator是怎么实现遍历操作的

iterator 会创建一个指针对象,每次通过调用指针对象的 next 方法,移动指针,从而遍历整个数据结构。next 方法每次都会返回一个对象,对象中包含两个属性,一个是 value,代表当前指针所指向的当前的值,一个是 done,done 是一个布尔类型值,true 代表后面没有数据了,则指针不会继续向后移动指针,遍历结束,false 代表后面还有数据,指针将会继续后移,知直到遍历结束。

通过输出 value 值,达到遍历数据结构的目的。注意,当指针指向最后一个元素的时候,done 为 true,value 则为 undefined。


实现一个 iterator

下面通过 ES5 的语法实现一个 iterator

function createIterator(arr) {
    var index = 0;
    return {
        next: function() {
            // 判断index是否大于当前数据结构的长度
            var done = (index  >= arr.length);
            //  如果是index大于了,返回undefined
            //  否则,将当前位置值存储
            var value = done ? undefined : arr[index ++];
            //  返回一个对象
            return {
                done: done,
                value: value
            };
        }
    };
}
var iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); 
// "{ value: 1, done: false }"
console.log(iterator.next()); 
// "{ value: 2, done: false }"
console.log(iterator.next()); 
// "{ value: 3, done: false }"
console.log(iterator.next()); 
// "{ value: undefined, done: true }"
// 之后的所有调用
console.log(iterator.next()); 
// "{ value: undefined, done: true }"

for...of循环

for...of 循环是专门为 iterato r而生的,for...of 循环的值必须是一个iterable (可迭代的),或者它可以转换为一个 iterable 对象。

以下是使用 for...of 循环遍历一个数组:

var arr = ['a','b','c','d','e']
for(let i of arr){
  console.log(i)
} 
// 'a'  'b'  'c'  'd'  'e'

也可以使用 ES5 实现 for...of 循环

var arr = ['a','b','c','d','e']
key = Object.keys(arr)
for(let i=0;i<key.length;i++){
  // key是数组arr的所有key值的数组,即['0','1','2','3','4']
  console.log(arr[key[i]])
} 
// 'a'  'b'  'c'  'd'  'e'

实际上,for..of 循环时,会向循环对象请求一个迭代器,然后反复调用这个迭代器把它产生的值赋给循环变量。

此处顺便对比一个 for...in 循环的区别:

var arr = ['a','b','c','d','e']
for(let i in arr){
  console.log(i)
} 
// 0 1 2 3 4

可以看到 for...in 循环是在数组 arr 的键值上,也就是索引值上循环,for...of是在 arr 的 value 上循环,也就是索引对应的值。

for..of 适合遍历数组对象、字符串、map和set,for-in适合遍历对象。


什么样的数据结构的可遍历的(iterable)

ES6 规定,只要包含 Symbol.iterator 属性的数据结构就是可遍历的,在当前数据结构的原型链上具备也可以。Symbol.iterator 属性本身是一个函数,用于生成当前数据结构的iterator对象。每次执行这个函数都会返回一个iterator。

至于属性名 Symbol.iterator,它是一个表达式,返回 Symbol 对象的 iterator 属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内。有关 Symbol 对象的详细介绍,请移步本系列文章:ES6 新特性梳理系列丨Symbol - 新的数据类型


默认具备iterator接口的数据结构

在ES6中,为一些数据结构默认提供了 iterator 接口,分别是:

Arrays(数组)、Strings(字符串)、Generators(生成器)、Collection(集合),我们可以对这些方法直接使用for...of循环进行遍历操作。

下面我们为一个默认不具备 iterator 接口的数据结构,添加 Symbol.iterator 方法,以实现 for...of 遍历操作

// 添加symbol.iterator方法之前
let obj = {data: [ 'hello', 'world' ]}
for(let i of obj){
  console.log(i)
}
// TypeError: obj is not iterable


// 为obj添加iterator方法
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 };
        }
      }
    };
  }
};
for(let i of obj){
  console.log(i)
}
// hello
// world

对于类数组对象,可以直接引用 Array 原型上的 Symbol.iterator 方法快速实现可遍历操作。

let a = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3, // length 属性是必须的
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let i of a) {
  console.log(i); 
}
// 'a', 'b', 'c'

ES6 新特性梳理系列文章将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!

叶阳辉

HFun 前端攻城狮

往期精彩:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值