ES6-迭代器和生成器

107 阅读3分钟

什么是迭代

迭代就是从一个数据集合中,不断取出数据。

迭代器

ES中是一个对象,改对象有一个next方法,并且next方法中返回一个对象,这个对象有value和done属性。

const iterator = {
    next(){
        return {
            value:xxx,// 下一个的值
            done:Boolean, // 是否迭代完成
        }
    }
}

使用迭代器遍历数组

const arr = [1, 2, 3, 4, 5];
const iterator = {
    i: 0,
    next() {
        const result = {
            value: this.i >= arr.length ? '' : arr[this.i],
            done: this.i >= arr.length,
        }
        this.i++;
        return result;
    }
}

console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

/**
    { value: 1, done: false }
    { value: 2, done: false }
    { value: 3, done: false }
    { value: 4, done: false }
    { value: 5, done: false }
    { value: '', done: true }
    { value: '', done: true }
 */

迭代器创建器

function createIterator(arr) {
    let i = 0;
    const iterator = {
        next() {
            const result = {
                value: i >= arr.length ? '' : arr[i],
                done: i >= arr.length,
            }
            i++;
            return result;
        }
    }
    return iterator;
}
const arr1 = [1, 2, 34, 45, 100, 300, 400];
const arr2 = [2, 3, 4];
const iterator1 = createIterator(arr1);
let iter1 = iterator1.next();
while (!iter1.done) {
    console.log(iter1.value);
    iter1 = iterator1.next();
}
console.log('迭代完成');

使用迭代器获取斐波那契数列

function createFibonacciIterator() {
    let i = 1,
        prev2 = 1,
        prev1 = 1,
        value;
    return {
        next() {
            if (i <= 2) {
                value = 1;
            } else {
                value = prev2 + prev1;
            }
            const result = {
                value,
                done: false
            }
            let temp = prev1;
            prev1 = value;
            prev2 = temp;
            i++;
            return result;
        }
    }
}
const iterator = createFibonacciIterator();

console.log(iterator.next()); // 1
console.log(iterator.next()); // 1
console.log(iterator.next()); // 2
console.log(iterator.next()); // 3
console.log(iterator.next()); // 5
console.log(iterator.next()); // 8
console.log(iterator.next()); // 13
console.log(iterator.next()); // 21
console.log(iterator.next()); // 34

可迭代协议

如果一个对象具有知名符号属性Symbol.iterator,该属性返回一个迭代器创建函数,那么该对象就是可迭代的。

js中的数组就满足可迭代协议。

image.png

for of

for of循环可以用来遍历可迭代对象。

const obj = {
    a: '1',
    b: '2',
    [Symbol.iterator]() {
        const keys = Object.keys(this);
        let i = 0;
        return {
            next: () => {
                const value = {
                    propName: keys[i],
                    propValue: this[keys[i]]
                }
                const result = {
                    value,
                    done: i >= keys.length,
                }
                i++;
                return result;
            }
        }
    },
}

for (const item of obj) {
    console.log(item);
    // { propName: 'a', propValue: '1' }
    // { propName: 'b', propValue: '2' }
}

展开运算符

利用展开运算符,可以将可迭代对象展开到数组中。

const a = [...obj];
console.log(a);

/**
 * [
        { propName: 'a', propValue: '1' },
        { propName: 'b', propValue: '2' }
    ]
 * 
 */

生成器(generator)

生成器既是一个迭代器也是可迭代对象。

创建一个生成器

function *createGenerator(){
    
}

该函数就返回一个生成器。

yield

yield关键字只能在生成器创建函数中使用。

image.png

只有调用了next方法,生成器创建函数里面才会运行,调用几次next方法,就运行到相应的yield关键字后面,后面代码只有等下次调用next方法才会运行,当函数执行完成后,返回的生成器的状态done才会变为true。

next方法

image.png

生成器函数外部可以向 next 方法传递一个参数,这个参数会被当作上一个 yield 表达式的返回值,如果不传递任何参数,yield 表达式返回 undefined

由于传递的参数被当作上一个yield表达式,所以第一次调用next传递的参数就不管用了。

return方法

return方法会提前结束迭代过程,如果有参数就作为迭代器对象的value值。

function *createGenerator() {
    yield 1;
    yield 3;
    yield 4;
    yield 5;
}

const generator = createGenerator();
const g = generator.next();
console.log(g);
console.log(generator.return());
console.log(generator.next());

// { value: 1, done: false }
// { value: undefined, done: true }
// { value: undefined, done: true }

catch

function *createGenerator() {
    yield 1;
    yield 3;
    yield 4;
    yield 5;
}

const generator = createGenerator();
const g = generator.next();
console.log(g);
console.log(generator.throw('error')); // 报错
console.log(generator.next());

// { value: 1, done: false }
// 报错

利用生成器创建迭代器

function *createIterator(arr){
    for (const item of arr) {
        yield item;
    }
}
const arr = [1,2,3,4];
const iterator = createIterator(arr);
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())

/* { value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: 4, done: false }
{ value: undefined, done: true }
{ value: undefined, done: true }
{ value: undefined, done: true } */

斐波拉契函数

function* createFibonacci() {
    let prev2 = 1,
        prev1 = 1,
        n = 1;
    while (true) {
        if (n <= 2) {
            yield 1;
        } else {
            const sum = prev2 + prev1;
            yield sum;
            prev1 = prev2;
            prev2 = sum;
        }
        n++;
    }
}
const fi = createFibonacci();

console.log(fi.next());
console.log(fi.next());
console.log(fi.next());
console.log(fi.next());
console.log(fi.next());
console.log(fi.next());
console.log(fi.next());

/* { value: 1, done: false }
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: 5, done: false }
{ value: 8, done: false }
{ value: 13, done: false } */

使用生成器模拟async/await

/**
 * 模拟async/await
 * @param {Generator} generatorFunc 生成器函数
 */
function runTask(generatorFunc) {
    const generator = generatorFunc();
    let result = generator.next(); // 开始迭代 {value: 1, done: false }
    handleResult();
    // 处理result
    function handleResult() {
        // 迭代结束
        if (result.done) {
            return;
        }
        // 迭代未结束 1.返回的是Promise,2.不是promise
        if (typeof result.value.then === 'function') {
            result.value.then(res => {
                result = generator.next(res); // {value: undefined, done: true}
                handleResult();
            }, error => {
                result = generator.next(error);
                handleResult();
            });

        } else {
            // next的参数会传给 yield表达式返回值
            result = generator.next(result.value); // { value: promise, done: false }
            // 继续处理后续
            handleResult();
        }
    }
}

function* task() {
    const a = yield 1;
    console.log(a);
    const res = yield new Promise((resolve, reject) => {
        console.log('开始')
        setTimeout(() => {
            resolve('generator test')
        }, 2000);
    });
    console.log(res);
}

runTask(task);

实现一个获取质数的函数(闭包的应用)

function isPrime(n) {
    let flag = 0;
    for (let i = 1; i <= n; i++) {
        if (n % i === 0) {
            flag++;
        }
    }
    return flag === 2;
}


function getNextPrime() {
    let i = 0;
    return function () {
        i++;
        while (!isPrime(i)) {
            i++;
        }
        return i;
    }
}
const getPrime = getNextPrime();
console.log(getPrime());//2 
console.log(getPrime());//3
console.log(getPrime());//5
console.log(getPrime());//7
console.log(getPrime());//11
console.log(getPrime());//13