es6之迭代器、生成器

迭代器(iterator)

簡單來講,迭代器(iterator)就是一個知足必定規則的對象。數組

規則:若是一個對象中含有一個next方法,next方法中返回一個對象,其中中包含兩個屬性valuedone,那麼這個對象就是一個迭代器。瀏覽器

以下代碼就是一個迭代器:dom

//生成隨機數的迭代器
let iterator = {
    total: 2,
    i: 1,
    next(){
        let obj = {
            value: this.i > this.total ? undefined : Math.random(), //下一個數據的值
            done: this.i > this.total, //是否還有下一個數據
        }
        this.i++;
        return obj;
    }
}

執行方式,調用next函數異步

console.log(iterator.next()) //{value: 1, done: false}
console.log(iterator.next()) //{value: 2, done: false}
console.log(iterator.next()) //{value: undefined, done: true}

遍歷迭代器數據函數

let next = iterator.next();

while(!next.done){
    console.log(next.value);
    next = iterator.next();
}

迭代器建立函數(iterator creator)

一個返回迭代器對象的函數。this

function createIterator(total = 2){
    let i = 1;
    return {
        next(){
            let obj = {
                value: i > total ? undefined : Math.random(), //下一個數據的值
                done: i > total, //是否還有下一個數據
            }
            i++;
            return obj;
        }
    }
}

const iterator = createIterator(5)

將一個數組改爲迭代模式:code

function createArrIterator(arr){
    let i = 0;
    return {
        next(){
            let obj = {
                value: arr[i++],
                done: i > arr.length
            }
            return obj;
        }
    }
}

const iterator = createArrIterator([3, 4,5, 23, 4, 6, 34]);

迭代協議

只有知足迭代協議的對象才能使用for of遍歷,不然就會報錯。對象

const obj = {}

for(const i of obj){
    console.log(i) // "Uncaught TypeError: obj is not iterable"
}

若是想使用for of遍歷obj,只要將obj改形成知足可迭代協議的對象便可。開發

迭代協議:知足可迭代協議的對象要有一個符號屬性(Symbol.iterator),屬性的值是一個無參的迭代器建立函數。以下所示:get

//obj加入[Symbol.iterator]屬性後可被迭代
const obj = {
    [Symbol.iterator]: (total = 2) => { //迭代器建立函數
        let i = 1;
        return {
            next() {
                let obj = {
                    value: i > total ? undefined : Math.random(), //下一個數據的值
                    done: i > total, //是否還有下一個數據
                }
                i++;
                return obj;
            }
        }
    }
}

for(const i of obj){
    console.log(i); // 不報錯
}
ArrayMapSet可使用 for of遍歷,是由於他們的原型上都有符號屬性 (Symbol.iterator)

for of執行原理

實際上for of遍歷可迭代對象時,就是運行該對象上的Symbol.iterator屬性,而後調用迭代器的next方法直到done屬性是true位置。

const obj = {
    [Symbol.iterator]: (total = 2) => {
        let i = 1;
        return {
            next() {
                let obj = {
                    value: i > total ? undefined : Math.random(), //下一個數據的值
                    done: i > total, //是否還有下一個數據
                }
                i++;
                return obj;
            }
        }
    }
}


for(const i of obj){
    console.log(i); // 不報錯, i實際上就是迭代器next方法返回對象裏面的value屬性
}

//for of 代碼至關於以下代碼:
const iterator = obj[Symbol.iterator]();
let item = iterator.next();
while(!item.done){
    const i = item.value;
    console.log(i) //執行for of中的代碼
    item = iterator.next()
}

生成器 generator

由構造函數Generator建立的對象,建立的對象是一個迭代器,也知足可迭代協議。

Generator構造函數是瀏覽器內置的,開發者沒法使用。
//僞代碼
const generator = new Generator();

//generator的內部大概是這樣的:
generator = {
    next(){...}, //具備next函數
    [Symbol.iterator](){...}, //具備(Symbol.iterator)屬性
}

生成器建立函數(generator function)

開發者沒法直接調用Generator建立生成器,只能使用生成器建立函數建立一個生成器。

生成器函數定義:只要function關鍵字和函數名之間加入一個*,該函數就是生成器函數,會返回一個生成器。

function *createGenerator(){}

const generator = createGenerator(); //獲得一個生成器

console.log("next" in generator); //true
console.log(Symbol.iterator in generator); //true
console.log(generator.next === generator[Symbol.iterator]().next); //true

yield關鍵字

寫在生成器內部,至關於暫停,配合next方法能夠在生成器外部控制生成器函數內部代碼的執行。

  1. yield關鍵字只能在生成器內部使用;
  2. 每次調用生成器的next方法後,代碼會從上一個yield執行到下一個yield
  3. 若是沒有生成器函數內部沒有yield關鍵字,則裏面的代碼一行都不會執行;
  4. return返回的值做爲next方法的done第一次爲true時的value值;
  5. yield關鍵字後面的值會當作執行next方法時返回的value值;
  6. yield關鍵字表達式返回的值等於執行next方法時傳入的參數。

舉個例子:你們最好使用瀏覽器,手動調用generator.next(),一步一步看比較容易理解。

function* createGenerator() {
    let res;
    console.log("開始", res);
    
    res = yield 1; //將第2個next傳入的參數做爲返回值賦值給res

    console.log("打印1", res);
    
    res = yield 2; //將第3個next傳入的參數做爲返回值賦值給res
    
    console.log("打印2", res);
    
    return "結束"
}

const generator = createGenerator();
let result = generator.next();
console.log("調用第1次next的返回值:", result);

result = generator.next(result.value);
console.log("調用第2次next的返回值:", result);

result = generator.next(result.value);
console.log("調用第3次next的返回值:", result);

result = generator.next(result.value);
console.log("調用第4次next的返回值:", result);

//輸出:
開始 undefined
調用第1次next: { value: 1, done: false }
打印1 1
調用第2次next: { value: 2, done: false }
打印2 2
調用第3次next: { value: '結束', done: true }
調用第4次next: { value: undefined, done: true }
利用生成器函數將異步代碼轉化爲同步代碼:
/**
 * 模擬一個請求
 */
function getData() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve('數據');
        }, 2000)
    })
}

/**
 * 生成器函數,在這裏寫業務邏輯,能夠將異步代碼的寫法轉化爲同步代碼寫法
 */
function* task() {
    console.log('獲取數據中...');
    let result = yield getData(); //將異步代碼轉化爲同步的寫法
    console.log('獲得數據:', result);
    //對數據進行後續處理...
}

/**
 * 運行生成器的通用函數
 */
function run(generatorFunc) {
    const generator = generatorFunc();
    next();

    function next(nextValue) {
        let result = generator.next(nextValue)
        if (result.done) { //迭代結束
            return;
        } else {
            const value = result.value;
            if (value instanceof Promise) {
                value.then(data => next(data));
            } else {
                next(value);
            }
        }
    }
}

run(task); //執行生成器函數代碼

//輸出:
獲取數據中...
獲得數據: 數據
相關文章
相關標籤/搜索