【JS基本】ES6语法

iterator迭代器

在ES6之前遍历数组的要领有以下四种:

// 第一种
for(var i = 0; i < array.length; i++){
    console.log(array[i])
}
// 第二种
array.forEach(function(item,index){
    console.log(item)
})
// 第三种
for(var index in array){
    console.log(array[index])
}
// 第四种
for(var value of array){
    console.log(value)
}

在上面的遍历体式格局中,第二种体式格局有一种小缺点,就是不能运用break语句中缀实行,也不能运用return语句返回到外层函数。它会一向遍历完数组的一切元素。第三种体式格局是一个更蹩脚的体式格局,在遍历历程当中,赋值给index的不是number范例,而是字符串范例,for-in轮回除了遍历数组外,还会遍历自定义属性,以至是遍历出原型链上的属性。而且for-in的遍历递次不能获得保证。

第四种要领是ES6中新增的遍历数组的要领,它可以准确相应break、continuereturn语句,for-in语句除了能遍历数组外,还能遍历类数组对象,如DOM的NodeList对象,arguments对象,也能遍历字符串、Set对象、Map对象。

for-of轮回语句可以遍历种种鸠合的。是因为这些对象都有一个迭代器的要领,迭代器(Iterator)是一种接口,为种种差别的数据构造供应一致的接见机制,任何数据构造只需布置了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(var value of obj){
    console.log(value)    // 输出   hello  world
}

迭代器也可以直接运用Array默许的iterator。

// 也可以直接运用Array的迭代器
let newObj = {
    0:'a',
    1:'b',
    2:'c',
    [Symbol.iterator]:Array.prototype[Symbol.iterator]
}
for(var value of newObj){
    console.log(value)   // 输出 a b c
}

for-of轮回语句实际上是挪用遍历对象的[Symbol.iterator]要领,该要领返回一个iterator,内里有一个next要领,for轮回会不停挪用这个iterator.next要领来猎取下一个值,直到返回值中的done属性为ture时终了轮回。除了增加iterator外还可以运用yield完成轮回:

let obj = {
    [Symbol.iterator]: function* (){
        for(var i = 0; i < 100; i++){
            yield i
        }
    }
}
for(var value of obj){
    console.log(value)
}

iterator的遍历历程

  1. 建立一个指针对象,指向当前数据构造的肇端位置,也就是说,遍历器对象的实质就是一个指针对象。
  2. 第一次挪用指针对象的next要领,可以将指针指向数据构造的第一个成员。
  3. 不停挪用指针对象的next要领,直到它指向数据构造的终了位置。

当运用for-of轮回遍历某个数据构造时,该轮回会自动去寻觅iterator接口。只需一个对象含有iterator接口,那末该对象就可以被遍历。

运用iterator的场景

  • 解构赋值
let arr = [1,2,3,5]
let [first,...second] = arr
  • 扩大运算符
var str = 'hello'
[...str]    //  ['h','e','l','l','o']
  • yield*

yield*背面跟的是一个可遍历的构造,它就会挪用该构造的遍历器接口。

let generator = function* (){
    yield* [2,3,4]
}
  • 其他场所

因为数组的遍历会挪用遍历器接口,所以任何接收数组作为参数的场所,实在都挪用了遍历器接口。比方for-of、Promise.all()、Promise.race()

天生器Generators

天生器函数与一般函数差别的点:一般函数运用function关键字声明,天生器函数运用function*声明,在天生器函数内部,有相似return语法的yeild关键字,与return差别的是,天生器函数可以yeild屡次,在函数实行历程当中,碰到yield表达式马上停息,后续可恢复实行状况。

function* question(name){
    yield "你好" + name + '!'
    yield "愿望你能喜好"
    yield "下次再会"
    return '拜拜'
}
var iter = question('小明')
iter.next()    // {value: "你好小明!", done: false}
iter.next()   // {value: "愿望你能喜好", done: false}
iter.next()    // {value: "下次再会", done: false}
iter.next()    // {value: '拜拜', done: true}

generator函数在挪用后,并不会马上实行,而是返回一个iterator对象,每次天生器实行到yield语句后,天生器函数的客栈构造(当地变量、参数、临时价、天生器内部当前的实行位置)被移除客栈。但是,天生器对象保留了对这个客栈构造的援用,所以稍后挪用.next()要领可以从新激活客栈构造而且继承实行。不过天生器不是线程,它依然处于JS单线程里。

假如运转到背面没有yield表达式,就会一向运转到函数终了,直到return语句为止,并将return表达式的值赋值给末了返回对象的value值,假如没有return语句,则返回对象的value值为undefined。
generator天生器是iterator的天生函数,实行generator函数,返回的就是iterator迭代器。

next要领的参数

function* gen(){
    for(let i = 0; true; i++){
        let reset = yield i
        if(reset){
            i = -1
        }
    }
}
let g = gen()
g.next()     // {value:0,done:false}
g.next(true)    // {value:0;done:false}

yield表达式自身没有返回值,或者说老是返回undefined,next要领可以带一个参数,该参数就会被作为上一个yeild表达式的值。generator从停息状况到恢复运转,它的上下文状况是稳定的,经由过程next要领传入参数,可以在generator函数最先运转今后,继承向函数内部注入值。

throw要领

function* gen(){
    try {
        yield '123'
    }catch(e){
        console.log('内部捕捉',e)
    }
}
let g = gen()
g.next()
try {
    g.throw('a')
    g.throw('b')
}catch(e){
    console.log('外部捕捉','b')
}
// 内部捕捉 a
// 外部捕捉 b

上述代码中,遍历器对象g一连抛出毛病,第一个毛病被generator函数体内的catch语句捕捉,第二次抛出毛病时,因为catch语句已实行过了,不会捕捉该毛病,所以这个毛病由函数体外的catch捕捉。假如函数内部没有try-catch语句,那末throw要领抛出的毛病将被外部的try-catch捕捉。遍历器对象g挪用throw要领后,抛出的异常被generator函数体捕捉今后,会附带实行下一条yield语句。一旦generator实行历程抛出毛病,且没有被内部捕捉,generator函数就不会实行下去了。假如今后再挪用next要领,将返回对象{value:undefined,done:true}

return要领

function* gen(){
    yield 1;
    yield 2;
    yiled 3;
}
var g = gen()
g.next()      // {value:1,done:false}
g.return('end')  // {value:'end',done:true}
g.next()     // {value:undefined,done:true}

假如generator函数内部有try-finally代码块,那末return要领会推晚到finally代码块实行终了再实行。

function* numbers () {
  yield 1;
  try {
    yield 2;
    yield 3;
  } finally {
    yield 4;
    yield 5;
  }
  yield 6;
}
var g = numbers();
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.return(7) // { value: 4, done: false }
g.next() // { value: 5, done: false }
g.next() // { value: 7, done: true }

next()、throw()、return()这三个要领都是让generator函数恢复实行,而且运用差别的语句替代yield表达式,next()是将yeild表达式替代成一个值,throw()是将yeild替代成一个throw语句,return()是将yield语句替代成一个return语句。

yield*

在generator函数内部再挪用另一个generator函数,默许状况下是没有结果的,这个时刻就需要用到yield*表达式,用来在一个generator函数内实行另一个generator函数。

    原文作者:熟悉的陌生人
    原文地址: https://segmentfault.com/a/1190000013633217
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞