这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战
1, 前言
ECMAScript是标准,JavaScript是这个标准的实现和扩展。
ECMAScript定义了:语言语法,类型, 原型和继承,内建对象和函数的标准库,ECMAScript涵盖了各种环境中JS的使用场景,无论是浏览器环境还是类似nodejs的非浏览器环境。
ECMAScript标准的历史版本分别是:1,2,3,5
2009年发布的ES5引入了新的特性: Object.create(), Object.defineProperty(), getters, setters, 严格模式, 以及JSON对象,还有最实用的数组方法: .map(), .filter()方法
经过持续几年的磨砺, 2015年发布的ES6,让JS得到有史以来最实质的升级,它的新特性和语法糖更是让人爱不释手,箭头函数,字符串插值,代理,生成器,ES6已经彻底的改变了我们编写JS的习惯。
2, 数组遍历
- (1) JavaScript刚萌生时,最常用的方法
for(var i = 0; i < myArry.length; i++) {
console.log(myArry[i])
}
- (2) ES5发布之后,你会想到内建方法:
forEach遍历数组
myArry.forEach(function(value) {
console.log(value)
})
forEach有个缺点: 不能使用breack语句中断循环,也不能使用return语法返回到外层函数
- (3) 你有可能向尝试一下
for-in循环, 切记:千万不要这样
for(var index in myArray) { // 千万不要这样
console.log(myArray[index])
}
首先:index的值实际上不是一个数字,而是字符串 "0", "1", "2",,,,有可能无意之间进行了字符串算数计算
其次: for-in 循环除了遍历数组元素外,还会遍历自定义属性,甚至连数组原型链上的属性都能被访问到
最后: for-in 是为普通对象设计的,你可以遍历得到字符串类型的健,不适用数组的遍历
- (4) ES6为两解决
for-in遍历数组的问题,新增了一个语法for-of
for(var value of myArray) {
console.log(value)
}
这种循环看起来是否很眼熟,当然for-of还有其他强大的功能
1, 这是最简洁,最直接的遍历数组元素的方法
**2, 这个方法避开了for-in循环的所有缺陷 **
3, 与forEach()不同,for-of可以正确响应break, continue, return 语句
4, for-in循环用来遍历对象属性
5,for-of循环用来遍历数据,数组中的值,当然,不仅如此
for-of其他特性
for-of循环不仅支持数组,也支持类数组对象,也支持字符串遍历,同样支持Map, Set对象遍历
举个例子1:Set对象
// Set对象可以自动排除重复项
let myArray = [1,0,0,2,9,8,3,1];
var uniqueItem = new Set(myArray)
for(var value of uniqueItem) {
console.log(value)
}
举个例子2: Map对象
// Map对象中的数据是有键值对组成,所以需要使用解构
for(var [key, value] of MapObject) {
console.log(key, value)
}
总之: 你只需要记住,未来的JS可以使用很多新型的集合类,而for-of就是为遍历所有这些集合特别设计的循环语句, for-of循环不支持普通对象,如果你想循环遍历一个对象的属性,可以使用for-in或者内建方法 Object.keys()方法
3,Symbol.interator
你可以给任意类型的对象添加迭代器方法。
比如:你给对象添加myObject.toString()方法,就可以将对象转化为字符串
当你给任意对象添加myObject[Symbol.interator]()方法,就表示:这个对象可以被for-of循环遍历啦。
4, 迭代器
简单的理解:迭代器是一个拥有.next()方法的特殊对象,每次调用next()方法都返回一个结果对象。
注意: 迭代器对象可以是任意具有.next()方法的对象。
举个例子: 通过for-of遍历的时候,首先调用集合的[Symbol.interator]()方法,紧接着返回一个新的迭代器对象,这个迭代器对象可以是任意具有.next()方法的对象, for-of循环将重复调用这个.next()方法
var myIterator = {
[Symbol.interator]: function() {
return this;
},
next: function() {
return { done: false, value: 0 }
}
}
在ES6中,迭代器的编写规则比较复杂,为了让创建迭代器的过程变得简单,ES6同时还引入了一个生成器对象,它可以让创建迭代器对象的过程变的更简单。
5, 生成器
普通函数使用function声明, 而生成器函数使用function*声明
在生成器函数内部,有一种类似return的语法:关键字yield。二者的区别: 普通函数只可以return一次,而生成器函数可以yield多次,在生成器执行的过程中,遇到yield表达式立即暂停,后续可回复执行状态,普通函数不能自动暂停,生成器函数可以。
举个例子:一个简单的生成器
function* quips(name) {
yield "你好" + name
yield "你也好"
yield "我们下次再见"
}
6, 生成器就是迭代器
因为:所有的生成器都有内建.next()和[Symbol.iterator]()方法的实现。开发过程你只需编写循环部分的行为。不借助生成器实现的迭代器会变的难以理解。
如何让作为迭代器的生成器发挥最大的作用?
编写生成器函数遍历这个对象,运行时yield每一个值,然后将这个生成器函数作为这个对象的Symbol.interator方法. 当你面对一个复杂的循环时,你可以拆分出生成数据的代码,将其独立转为独立的生成器函数,然后使用for(var data of myGenerator()) 遍历我们所需要的数据
ES6不提供用了过滤,映射以及针对任意可迭代数据集进行特殊操作的扩展库。借助生成器,很容易实现。举例:实现一个等效于Array.prototype.filter, 并支持DOM NodeLists方法
function* filter(test, iterable) {
for(var item of iterable) {
if(test(item))
yield item;
}
}
迭代器贯穿ES6的始终,它是数据和循环的新标准。
7, 简单的例子
生成器函数配合for-or实现数据的遍历
function* generator() {
yield 1;
yield 2;
yield 3;
yield 4;
}
for (const itemr of generator()) {
console.log(itemr)
}
输出结果:
1
2
3
4
总结:
当你调用一个生成器的时候,它并非立即执行,在遇到的最顶端第一行代码的时候,就停止了,立即冻结,而且返回了一个已暂停的生成器对象。每当你调用生成器对象的.next()方法时,函数调用将其自身解冻并一直运行到下一个yield表达式,再次暂停, 调用最后一个.next()时,返回结果中done为true, value为undefined