首先从远古讲起,刚出js的时候如何遍历一个数组呢?javascript
var arr = [1, 2, 3, 4, 7, 8, 9] for (let i = 0;i < arr.length;i++) { console.log(arr[i]); }
看起来笨的一批,因此ES5给你研究了一个foreach方法,可是这个方法不能break,也不能改变数组自身的值,也不能返回任何值。html
var arr = [1, 2, 3, 4, 7, 8, 9] var arr2 = arr.forEach((element, index) => { console.log('第' + index + '个数值为:' + element); return element * 2; }); console.log(arr2) // undefined console.log(arr1) // [1, 2, 3, 4, 7, 8, 9]
因此说foreach只给你最基本的操做,其余一律无论,若是你想要改变自身的值或者有break和countinue操做咱们可使用map操做,不展开讲了,以前专门写了篇博客总结了下。java
wzr:数组遍历方法总结ios
那么ES6专门为遍历数组提供了一种方法,就是for-of。说道for-of,不得不提到了for-inajax
那么关于他们俩的区别,我也专门写了一篇博客,不在这展开讲了。axios
wzr:深刻理解枚举属性与for-in和for-of数组
值得一提的是for-in是能够遍历数组的,可是不推荐用for-in去遍历数组,为何呢?由于for -in 返回的可枚举属性是字符类型,不是数字类型,若是"0","1"这样的属性和1,2数组发生相加,极可能不是直接相加,而是字符串的叠加。例如promise
const items = [1,2,3,4] for(item in items){ let tempitem = item+1 console.log(items[tempitem]) // undefined console.log(tempitem) // 01 21 32 41 item与数字相加 会获得字符串相加的结果,因此致使上面找不着items相应的项 }
因此为了不歧义,仍是不要用for-in遍历数组的好。浏览器
那么接下来就进入正题了,由于for-in在数组这边比较难用,因此ES6新添加的for-of来弥补for-in的不足。这是个正儿八经遍历数组的方法。与 forEach() 不一样的是,它支持 break、continue 和 return 语句。并且他自己的语法很是的简单:异步
for (variable of iterable) { //statements }
variable: 在每次迭代中,将不一样属性的值分配给变量。
iterable: 被迭代枚举其属性的对象。
并且关键的问题是for-of不只能够遍历数组,他能够遍历不少类数组对象。
而他的原理在于这些类数组对象中都有一个属性,就是Symbol.iterator
,也就是说,只要带Symbol.iterator
他都能遍历,咱们单独把这个属性拿出来,本身手动执行next()方法就会看到咱们成功的遍历了这个数组:
const items = [1,2,3,4] const giao = items[Symbol.iterator]() console.log(giao.next()) // {value: 1, done: false} console.log(giao.next()) // {value: 2, done: false} console.log(giao.next()) // {value: 3, done: false} console.log(giao.next()) // {value: 4, done: false} console.log(giao.next()) // {value: undefined, done: true}
同理,咱们能够已经过手动写一个iterator来更深刻的了解他的原理:
Array.prototype.myiterator = function(){ let i = 0 let items = this return { next(){ const done = i >= items.length const value = done ? undefined : items[i++] return { value, done } } } } const item = [1,2,3,4] // 控制台 > const giao = item.myiterator() //当咱们得到到遍历器时,咱们只须要代替for-of执行myiterator便可遍历这个数组。 > giao.next() {value: 1, done: false} > giao.next() {value: 2, done: false} > giao.next() {value: 3, done: false} > giao.next() {value: 4, done: false} > giao.next() {value: undefined, done: true}
效果跟for of是同样的。另外值得注意的是,你能够在任意对象里添加这个属性,让他们可遍历。
const items = [ 'blue', 'yellow', 'white', 'black'] for(item of items){ console.log(item) }
遍历器若是存在与一个对象内,他就可让这个对象可供for-of遍历,for-of的遍历方法就是不停地调用遍历器的next()
方法,直到done
属性变为true
。
为何拿生成器和遍历器一块儿讲呢,由于本质上,生成器器函数返回的就是一个遍历器!
生成器的语法很简单,就是在function后面加个*
,而后用yield来返回对应的值。(其实也能够将yield能够看作return,只不过须要next()来进行外调用,还有一个函数只能由一个return ,而yield能够有多个)
function* items(){ yield "1" yield "2" yield "3" } const num = items() // 控制台 > num.next() {value: "1", done: false} > num.next() {value: "2", done: false} > num.next() {value: "3", done: false} > num.next() {value: undefined, done: true} > num.next() {value: undefined, done: true}
那么咱们yield的以前一样也能够加入运算
function* items(){ let i =0 yield i // 0 i++ yield i // 1 i++ yield i // 2 i++ //这个就不运行了,由于他在yield以后 } const num = items() //不用浏览器控制台,直接打印也行 console.log(num.next()) // {value:0,done:false} console.log(num.next()) // {value:1,done:false} console.log(num.next()) // {value:2,done:false} console.log(num.next()) // {value:undefined,done:true}
利用这样的特性,咱们能够用Generator来进行ajax的等待操做。
function ajax(url){ // 请求成功自动调用next()方法。而后返回数据结果 axios.get(url).then(res => gen.next(res.data)) } function* step(){ const class = yield ajax.get(`http://laotie.com/getclass`) const score = yield ajax.get(`http://laotie.com/getscore?name=${class[0].name}) } // 得到这个函数的"遍历器" const gen = step() // 启动"遍历器",不启动就不会动 gen.next() // 获取class gen.next() // 获取到score
由于第二个get请求依赖第一个请求的结果,因此咱们解决办法第一个是运用Promise的回调来限制他们的前后顺序,可是在咱们学习了生成器以后咱们发现生成器的特性很适合作这样的事。也就是只有当第一个请求执行完以后,才能顺序执行第二个请求。
另外还有一些小的特性
*
能够添加到任意位置,都不会影响生成genterator。下面的写法都是能够的。
function * foo(x, y) { ··· } function *foo(x, y) { ··· } function* foo(x, y) { ··· } function*foo(x, y) { ··· }
关于Generator的Thunk或者co模块,由于ES8的async的加入,极大地简化了Genrtator的操做,针对原理和实战操做也就没有那么多要求了,接下来主要说一说async函数。
准确的说,async就是Generator的语法糖,首先看他的语法。
async function laotie(){ const data = await dosomething() }
能够看到,
*
由async
代替yield
由await
代替这样作的直接的好处就是显得更加语义化,可读性更强。可是其实async作到的远不止如此。
首先第一点,就是async不用不断执行next()了,async函数内置了执行器,使得咱们在调用函数时,只须要直接调用便可。以下:
// 接上一个代码块 laotie()
如今async 的await
仍是保留着等待的功能,可是由于没有了next()
,因此在调用await
不会像yield
那样返回值了。在async中,只有return返回,并且返回的是一个promise对象。
拿上面的代码直接改写成async加await的格式,
async function items(){ let i =0 await i i++ await i i++ await i i++ } console.log(items()) // Promise {<pending>}
直接调用方法咱们能看到返回的是一个状态为resolved的Promise对象,而不是Iterator。
而这个对象,返回的值就是函数里return出来的值。咱们能够用then()函数来接受这个值并打印他。
async function items(){ let i =3 return i } items().then(res=>{ console.log(res) // 3 })
固然这么举例子准定不是正经的用法,这些例子主要用于区分Generator和async函数以前的区别。
正确的用法如今是在await以后加入一个异步函数,await至关于将这个异步函数转化为同步函数,等这个异步函数执行完毕返回resolved的时候时候才往下执行进一步的操做。例如:
async function asyncPrint(value, ms) { await new Promise(resolve => { setTimeout(resolve, ms); }); console.log(value); } asyncPrint('hello world', 1000); // 一秒后打印hellow world
若是这个async 的函数中间有多个await,那么就让多个await以排队的方式执行。
用法2:
先让咱们把以前generator的例子拿过来
function ajax(url){ // 请求成功自动调用next()方法。而后返回数据结果 axios.get(url).then(res => gen.next(res.data)) } function* step(){ const class = yield ajax.get(`http://laotie.com/getclass`) const score = yield ajax.get(`http://laotie.com/getscore?name=${class[0].name}) } // 得到这个函数的遍历器 const gen = step() // 启动遍历器 gen.next()
写着挺累的,可是async能够快速的简化它。由于await接受的就是一个Prmoise函数,因此咱们能够直接在await后面使用axios,而后直接使用对象解构来获取相应的值。
async function step(){ const {data:{class}} = await axios.get(`http://laotie.com/getclass`) const {data:{core}} = await axios.get(`http://laotie.com/getscore?name=${class[0].name}) return {class,core} }
这样是否是就方便多啦!