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 前端攻城狮
往期精彩: