一文徹底搞懂迭代器與生成器函數(shù)

迭代器和生成器在前端業(yè)務(wù)里經(jīng)常有用到,但是可能感受不太明顯。特別是生成器,在react中如果你有用過redux中間件redux-saga那么你一定對(duì)生成器很熟悉。

本文是筆者對(duì)于迭代器與生成器的理解,希望在項(xiàng)目中有所幫助.

在閱讀本文之前,主要會(huì)從以下幾點(diǎn)去探討迭代器/生成器

迭代器是什么,想想為什么會(huì)有迭代器

生成器又是什么,它解決了什么樣的問題

以實(shí)際例子闡述迭代器與生成器

正文開始...

迭代器是什么

參考mdn上解釋,迭代器是一個(gè)對(duì)象,每次調(diào)用next方法返回一個(gè){done: false, value: ''},每次調(diào)用next返回當(dāng)前值,直至最后一次調(diào)用時(shí)返回{value:undefined,done: true}時(shí)結(jié)束,無論后面調(diào)用next方法都只會(huì)返回{value: undefined,done:true}

在過往的業(yè)務(wù)中,你一定用過for ... of循環(huán)過數(shù)組或者M(jìn)ap

const arr = [
    {
        name: 'Maic',
        age: 18
    },
    {
        name: 'Tom',
        age: 10
    }
]
for (let item of arr) {
    console.log(item);
    /* {name: 'Maic', age:18},{name: 'Maic', age:18} */
}
因?yàn)閿?shù)組就是可支持迭代器對(duì)象,并且for...of可以中斷循環(huán),關(guān)于循環(huán)中斷可以參考以前寫的一篇文章你不知道的JS循環(huán)中斷

因?yàn)閿?shù)組是支持可迭代的對(duì)象,如果使用迭代器獲取每組數(shù)據(jù)應(yīng)該怎么做呢?

const arr = [
    {
        name: 'Maic',
        age: 18
    },
    {
        name: 'Tom',
        age: 10
    }
]
const iterator = arr[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
我們執(zhí)行node index.js可以看到運(yùn)行結(jié)果


當(dāng)我們每次調(diào)用iterator.next()值時(shí),都會(huì)返回當(dāng)前的值,并且返回的值是{value: xxx, done: false},直至最后,返回的值{value: undefined, done: true }
不知道你發(fā)現(xiàn)沒有,上面迭代器,我是通過數(shù)組訪問Symbol.iterator方法,再調(diào)用返回的next方法,最后得到當(dāng)前的值

我們可以在控制臺(tái)看下


數(shù)組是有這個(gè)Symbol.iterator屬性的
從以上迭代器特征中,我們可以得知,數(shù)組是通過一個(gè)Symbol.iterator方法,返回一個(gè)next方法,并且next方法返回{value: xx, done: false},我們模擬一個(gè)迭代器

模擬迭代器

const iteratorObj = {
    value: [1, 2, 3],
    count:-1,
    next() {
        this.count++
        return {
            value: this.value[this.count],
            done: !this.value[this.count]
        }
    }
}
console.log(iteratorObj.next());
console.log(iteratorObj.next());
console.log(iteratorObj.next());
console.log(iteratorObj.next());
打印的結(jié)果依次是:

{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: undefined, done: true }
此時(shí)你會(huì)發(fā)現(xiàn)iteratorObj就基本實(shí)現(xiàn)了一個(gè)迭代器的基本功能。

我們用一個(gè)對(duì)象模擬了迭代器,但是我們發(fā)現(xiàn)這個(gè)對(duì)象迭代器貌似沒法復(fù)用

因此我們創(chuàng)建一個(gè)迭代器的工具函數(shù)

function createIteror(arr = []) {
    let count = -1;
    return {
        next: function () {
            count++
            return {
                value: arr[count],
                done: count >= arr.length
            }
        }
    }
}
const newCreateInteror = createIteror([1, 2, 3]);

console.log(newCreateInteror.next());
console.log(newCreateInteror.next());
console.log(newCreateInteror.next());
console.log(newCreateInteror.next());
結(jié)果是:

{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: undefined, done: false }
因此createIteror這個(gè)方法就具備了迭代器的功能

我們?cè)谶@之前用iteratorObj模擬了一個(gè)具備迭代器的功能,但是如何讓真正的對(duì)象支持迭代呢

讓對(duì)象支持迭代器功能

不知道你發(fā)現(xiàn)沒有,其實(shí)數(shù)組原型上是有Symbol.iterator,所以如果要讓一個(gè)對(duì)象支持迭代器功能,那么只需要遵循迭代協(xié)議即可

const coustomerInteror = {
    value: [1, 2, 3],
    // 讓對(duì)象支持迭代器協(xié)議,需要增加一個(gè)Symbol.iterator可訪問的方法,并返回一個(gè)迭代器對(duì)象,迭代器對(duì)象可以調(diào)用`next`方法,在next方法中返回一個(gè)當(dāng)前對(duì)象的值
    [Symbol.iterator]: function () {
        let count = -1;
        return {
            next: () => {
                count++;
                return {
                    value: this.value[count],
                    done: count >= this.value.length
                }
            }
        }
    }
}
const newInter = coustomerInteror[Symbol.iterator]();
console.log(newInter.next());
console.log(newInter.next());
console.log(newInter.next());
console.log(newInter.next());

for (let item of coustomerInteror) {
    console.log(item, '=result')
}
可以看到打印的結(jié)果


因此讓一個(gè)對(duì)象支持迭代器功能,只需要新增一個(gè)Symbol.iterator方法,遵循迭代器原則

支持所有對(duì)象可迭代

我們從以上結(jié)果得知要想一個(gè)對(duì)象支持迭代器功能,必須要有Symbol.iterator這樣的迭代器協(xié)議

因此我們可以在Object原型上新增這樣的一個(gè)迭代器協(xié)議

// 在Object.prototype原型上擴(kuò)展Symbol.iterator
Object.prototype[Symbol.iterator] = function () {
    let count = -1;
    return {
        next: () => {
            count++;
            const keys = Object.keys(this);
            return {
                value: this[keys[count]],
                done: count >= keys.length
            }
        }
    }
}
const cobj = { a: 1, b: 2 };
const iteror = cobj[Symbol.iterator]();
console.log(iteror.next());
console.log(iteror.next())
console.log(iteror.next())
for (let item of cobj) {
    console.log(item, '=rs')
}
const [a, b] = cobj;
console.log(a,b);
執(zhí)行的結(jié)果是:

{ value: 1, done: false }
{ value: 2, done: false }
{ value: undefined, done: true }
1 =rs
2 =rs
1 2
你會(huì)發(fā)現(xiàn)當(dāng)我們使用數(shù)組解構(gòu)時(shí),居然可以解構(gòu)對(duì)象的值

const [a, b] = cobj;
console.log(a,b);
本質(zhì)上就是我們迭代器會(huì)自動(dòng)調(diào)用iteror.next().value然后一一賦值返回了。

所以支持迭代器對(duì)象不僅可以for...of也可以被數(shù)組解構(gòu),這樣所有var obj = {}這樣類似申明的對(duì)象都可以支持迭代器了。

構(gòu)造函數(shù)支持可迭代






我們現(xiàn)在有個(gè)需求,需要支持通過構(gòu)造函數(shù)new出來的對(duì)象支持可迭代器功能

具體我們看下代碼

class Person {
    constructor() {
        this.name = 'Maic';
        this.age = 18;
    }
    [Symbol.iterator]() {
        let count = -1;
        return {
            next: () => {
                count++;
                // 獲取對(duì)象的所有屬性key
                const keys = Object.keys(this);
                return {
                    value: this[keys[count]],
                    done: count >= keys.length
                }
            }
        }
    }
}
const person = new Person();
const iter = person[Symbol.iterator]();
console.log(iter.next(), '==');
console.log(iter.next(), '==');
console.log(iter.next(), '==');
for (let item of person) {
    if (item === 'Maic') {
      break; // 可以中斷循環(huán)
    }
    console.log(item) // 這里并不會(huì)打印
}
const [name, age] = person;
console.log(name, age)
本質(zhì)上也是在構(gòu)造函數(shù)Person內(nèi)部新增了Symbol.iterator方法,并且返回了一個(gè)迭代器對(duì)象

打印的結(jié)果如下:

{ value: 'Maic', done: false } ==
{ value: 18, done: false } ==
{ value: undefined, done: true } ==
Maic 18
至此你應(yīng)該非常了解迭代器的對(duì)象的特性了哈

能夠for...of循環(huán)中斷,且能夠數(shù)組解構(gòu)、擴(kuò)展,所以你知道為啥會(huì)有迭代器了嗎?

那些原生API支持迭代器

首先是數(shù)組Array,Map,Set

只要是有迭代器特性,那么就可以被for...of,數(shù)組解構(gòu)等

生成器

這是es6新增的,參考generator[1]解釋,生成器是一種異步解決的方案,也可以理解一種函數(shù)內(nèi)部的狀態(tài)機(jī),能中斷函數(shù),也就是說能夠控制函數(shù)的運(yùn)行

具體我們以一個(gè)實(shí)際例子看下生成器是什么

function* genter() {
    yield 1;
    yield 2;
}
const gen = genter();
console.log(gen);
我們定義了一個(gè)普通函數(shù),但是這個(gè)普通函數(shù)比較特殊,前面有*,這就是定義生成器函數(shù),我們暫定把gen這個(gè)稱呼為生成器對(duì)象

然后我們打印生成器對(duì)象,實(shí)際是就像函數(shù)調(diào)用一樣,不過此時(shí)返回的是一個(gè)Object Generator

Object [Generator] {}
但是我們繼續(xù)看下

function* genter() {
    yield 1;
    yield 2;
}
const gen = genter();
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());

for (let item of gen) {
    console.log(item);
}
此時(shí)打印的結(jié)果是

{ value: 1, done: false }
{ value: 2, done: false }
{ value: undefined, done: true }
我們看下生成器函數(shù)內(nèi)部是有yield這樣的關(guān)鍵字

實(shí)際上這就是內(nèi)部函數(shù)的狀態(tài)機(jī),當(dāng)你使用用生成器時(shí),你調(diào)用next就會(huì)返回一個(gè)對(duì)象,并且像迭代器一樣返回{value: xxx, done: false}因此在使用上,我們必須認(rèn)清,生成器必須是帶有*定義的函數(shù),內(nèi)部是yield執(zhí)行的狀態(tài)機(jī)

當(dāng)我們調(diào)用函數(shù)生成器時(shí),并不會(huì)立即執(zhí)行,返回一個(gè)遍歷對(duì)象并返回一個(gè)next方法,當(dāng)遍歷對(duì)象調(diào)用next時(shí),就會(huì)返回yield執(zhí)行的狀態(tài)機(jī),并返回一個(gè)迭代器對(duì)象的值,yield會(huì)在當(dāng)前狀態(tài)暫停,只有調(diào)用next時(shí),就會(huì)執(zhí)行yield,yield

value表示當(dāng)前yield的值,done:false表示當(dāng)前遍厲沒有結(jié)束,如果繼續(xù)執(zhí)行g(shù)en.next()那么就會(huì)返回當(dāng)前的yield值,直到done:true時(shí),表示當(dāng)前遍歷對(duì)象已經(jīng)完全遍歷完畢。

我們?cè)賮砜聪逻@段代碼

function* start() {
  console.log('start')
}
const genterStart = start();
此時(shí)你會(huì)發(fā)現(xiàn)并不會(huì)調(diào)用start方法

但是你執(zhí)行下面代碼時(shí),才會(huì)立即調(diào)用

function* start() {
  console.log('start')
}
const genterStart = start();
setTimeout(() => {
    genterStart.next();
}, 1000)
我們會(huì)發(fā)現(xiàn)定時(shí)定時(shí)1s妙后才執(zhí)行start方法,而且是通過next去執(zhí)行的。

所以此時(shí)這個(gè)start變成了一個(gè)暫緩的執(zhí)行函數(shù),同時(shí)我們要注意yield只能用在*定義的生成器內(nèi)部

生成器-扁平化數(shù)組

我們?cè)谝酝臉I(yè)務(wù)中多少有寫過扁平化數(shù)組,通常也是用遞歸實(shí)現(xiàn)多維數(shù)組的打平,現(xiàn)在我們嘗試用生成器來實(shí)現(xiàn)一個(gè)扁平化數(shù)組

function* flat(arr) {
    for (let i = 0; i < arr.length; i++) {
        const item = arr[i];
        if (Array.isArray(item)) {
            // 如果是數(shù)組,則遞歸
            yield* flat(item)
        } else {
            yield item
        }
    }
}
const sourceArr = [1, [[2, 3], 4], [5, 6]]
const result = [];
for (let item of flat(sourceArr)) {
    result.push(item)
}
console.log(result)// [1,2,3,4,5,6]
但是這個(gè)flat貌似不太通用,因此可不可以像原生flat方法一樣,因此我們向下面這樣做,在Array的原型上新增一個(gè)方法,讓所有的數(shù)組都能訪問這個(gè)自定義方法

// Array的prototype中綁定一個(gè)公有方法
Array.prototype.$myFlat = function () {
    // 定義一個(gè)flat生成器
    function* flat(arr) {
        for (let i = 0; i < arr.length; i++) {
            const item = arr[i];
            if (Array.isArray(item)) {
                // 遞歸當(dāng)前flat
                yield* flat(item);
            } else {
                yield item
            }

        }
    }
    const ngen = flat(this);
    return [...ngen];
}
const sourceArr2 = [1, 2, [3, 4, 5, 6, [7, 8]]]

console.log(sourceArr2.$myFlat())



因此$myFlat這個(gè)方法就像原生flat一樣了

生成器與迭代器的關(guān)系

當(dāng)我們看到用*定義的方法,就變成一個(gè)生成器,此時(shí)我們調(diào)用這個(gè)生成器方法,那么此時(shí)就可以for...of循環(huán)了

  function* test() {
    yield 1;
    yield 2;
    yield 3;
}
const gtest = test();

// gtest.next() { value:1,done: false}
// for (let item of gtest) {
//     console.log(item) 這里相當(dāng)于已經(jīng)調(diào)用了gtest.next().value
// }
const [a, b, c, d] = gtest;
console.log('abc', a, b, c, d)
打印的結(jié)果就是:

abc 1 2 3 undefined
我們進(jìn)一步測試一下:

  function* test() {
    yield 1;
    yield 2;
    yield 3;
}
const gtest = test();
console.log(gtest[Symbol.iterator]() === gtest) // true
這里我們就會(huì)發(fā)現(xiàn)gtest可以通過Symbol.iterator這個(gè)方法直接調(diào)用,居然于它本身相等。


從控制臺(tái)中我們可以知道gtest返回就是一個(gè)生成器對(duì)象,它的構(gòu)造函數(shù)是GeneratorFunction,并且原型上有Symbol.iterator,而且是一個(gè)迭代器。
當(dāng)你使用

...
gtest[Symbol.iterator]().next();
gtest[Symbol.iterator]().next()
gtest[Symbol.iterator]().next()

// 以上等價(jià)于
/*
  gtest.next();
  gtest.next();
  gtest.next();
*/
可以看下控制臺(tái)打印的結(jié)果就知道了


所以大概了解生成器與迭代器的關(guān)系了么?本質(zhì)上是通過生成器對(duì)象的prototype的Symbol.iterator連接了起來

生成器函數(shù)的return

當(dāng)我們?cè)谏善骱瘮?shù)內(nèi)部return時(shí),那么當(dāng)調(diào)用next迭代完所有的值時(shí),繼續(xù)調(diào)用next,則會(huì)返回return的值

什么意思,我們看下下面這段代碼

function* test() {
    yield 1;
    yield 2;
    yield 3;
    return 4;
}
const gtest = test();
console.log(gtest.next());// {value: 1,done: false}
console.log(gtest.next()); // {value: 2,done: false}
console.log(gtest.next()); // {value: 3,done: false}
console.log(gtest.next()); // {value: 4,done: true}
console.log(gtest.next()); // {value: undefined,done: true}
yield后面可以是變量或者具體函數(shù)返回值 你可以這么寫

function* test() {
    let b = 2;
    const logNum = (num) => num
    yield 1;
    yield b;
    yield logNum(5);
    return 4;
}
const gtest = test();
console.log(gtest.next());
console.log(gtest.next());
console.log(gtest.next());
console.log(gtest.next());
執(zhí)行結(jié)果如下

{ value: 1, done: false }
{ value: 2, done: false }
{ value: 5, done: false }
{ value: 4, done: true }
{ value: undefined, done: true }
生成器傳參數(shù)

function* test() {
    let b = 2;
    const logNum = num => num
    const n = yield 1; // n為下面第二個(gè)yield(10)這里n = 10
    yield n * b; // 這個(gè)n就是第二個(gè)next傳入的,會(huì)把第一個(gè)yield當(dāng)返回值,傳給下個(gè)yield
    yield logNum(5);
    return 4;
}
const gtest = test();

console.log(gtest.next());
console.log(gtest.next(10));  // 20
console.log(gtest.next());
console.log(gtest.next());
/*
{ value: 1, done: false }
{ value: 20, done: false }
{ value: 5, done: false }
{ value: 4, done: true }
{ value: undefined, done: true }
*/
生成器捕獲異常






主要是在yield捕獲異常,具體看下下面這個(gè)簡單的例子


function* test() {
    try {
        yield 1;
    } catch (error) {
        console.log(error)
    }
    try {
        yield 2;
    } catch (error) {
        console.log(error, '---2');
    }
}
const gen = test();
console.log(gen.next())
gen.throw('錯(cuò)誤了');
console.log(gen.next()) // 并不會(huì)運(yùn)行

當(dāng)我們執(zhí)行g(shù)en.next()時(shí)會(huì)執(zhí)行yield 1此時(shí)返回{value: 1, done: false}當(dāng)我們執(zhí)行g(shù)en.throw時(shí),此時(shí)yield 2會(huì)暫停,并且就會(huì)中斷了。并且后面的gen.next()就是默認(rèn)返回{value: undefined, done: true}

yield狀態(tài)機(jī)

我們?cè)谶@之前都見過yield只能在生成器中使用,那到底有哪些使用,我們寫個(gè)例子熟悉一下


function* a() {
    yield 1;
    yield 2;
}

function* b() {
    yield* a();
    yield 3;
}

const bGen = b();
// console.log([...bGen]); [1,2,3]
// console.log(bGen.next()) 注意這個(gè)與上面不能同時(shí)使用,不然這個(gè)bGen就是返回{value: undefined, done: true}
yield后面能是函數(shù)返回值,能是變量,也可以是一個(gè)生成器函數(shù)

讓一個(gè)對(duì)象的方法支持生成器

const obj = {
    * getName() {
        yield 'Maic'
    }
}
const person = obj.getName();
console.log(person.next()); // {value: 'Maic', done: false}
等價(jià)

const obj = {
    getAge: function *() {
        yield 18
    }
}
const age = obj.getAge();
console.log(age.next()); // {value: 18, done: false}
生成器不能為new

function* a() {
    yield 1;
    yield 2;
}
// new a() error
生成器異步操作

在以往業(yè)務(wù)中肯定有這種場景,點(diǎn)擊頁面首先加載loading,然后請(qǐng)求數(shù)據(jù),當(dāng)數(shù)據(jù)請(qǐng)求成功后,就結(jié)束loading,我們看一段簡單的偽代碼

// 定義了一個(gè)獲取數(shù)據(jù)的生成器方法,setTimeout模擬異步請(qǐng)求
function* getList() {
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({
                code: 0,
                data: [
                    {
                        name: 'Maic',
                        age: 18
                    },
                    {
                        name: 'Web技術(shù)學(xué)苑',
                        age: 20
                    }
                ]
            })
        }, 1000)
    })
}
然后我們定義一個(gè)loadUI生成器

function* loadUI() {
    console.log('正在加載中...,開啟loading...');
    yield* getList();
    console.log('加載完成,關(guān)閉loading')
}
const loadStart = loadUI();
// 加載數(shù)據(jù),調(diào)用next().value 獲取yield的值
const currentData = loadStart.next().value;
currentData.then((res) => {
    if (res) {
        console.log(res);
    }
    loadStart.next(); // 關(guān)閉加載,加載完成,執(zhí)行yield 后面的代碼
});

或者你可以這樣

function* loadUI() {
    console.log('正在加載中...,開啟loading...');
    const { data } = yield getList();
    console.log(data);
    console.log('加載完成,關(guān)閉loading')
}

const loadStart = loadUI();
function getList() {
    const mockData = {
        code: 0,
        data: [
            {
                name: 'Maic',
                age: 18
            },
            {
                name: 'Web技術(shù)學(xué)苑',
                age: 20
            }
        ]
    };
    setTimeout(() => {
        loadStart.next(mockData);// next傳入數(shù)據(jù)當(dāng)成yield狀態(tài)機(jī)的返回值
    }, 1000)

}
// 繼續(xù)執(zhí)行yield后面的代碼
loadStart.next();
運(yùn)行的結(jié)果依舊是一樣的,這樣我就可以通過loadStart精準(zhǔn)的控制數(shù)據(jù)請(qǐng)求在哪里執(zhí)行了。如果我最后一行代碼不執(zhí)行,那么久不會(huì)執(zhí)行后面的打印代碼了,從而達(dá)到精準(zhǔn)的控制函數(shù)內(nèi)部的執(zhí)行。

控制多個(gè)函數(shù)按順序執(zhí)行

假設(shè)有一個(gè)場景,就是fn2依賴fn1的結(jié)果而決定是否是否執(zhí)行,fn3依賴fn2的狀態(tài)是否繼續(xù)執(zhí)行,那怎么設(shè)計(jì)呢?生成器可以幫我們解決這個(gè)需求問題

function fn1() {
    return {
        code: 1,
        message: '我是fn1,你成功了,請(qǐng)進(jìn)行下一步'
    }
}
function fn2() {
    return {
        code: 0,
        message: '我是fn2,失敗了'
    }
}
function fn3() {
    console.log('恭喜你,闖關(guān)成功了...');

}
const source = [fn1, fn2, fn3];
function* main(arr = []) {
    for (let i = 0; i < arr.length; i++) {
        yield arr[i]( "i")
    }
}
const it = main(source);
for (let item of it) {
    console.log(item)
    if (item.code === 0) {
        break;
    }
}
結(jié)果是:

{ code: 1, message: '我是fn1,你成功了,請(qǐng)進(jìn)行下一步' }
{ code: 0, message: '我是fn2,失敗了' }
當(dāng)fn2返回code:0就會(huì)終止break中止,當(dāng)fn2中返回的code是1時(shí),才會(huì)進(jìn)入下一個(gè)迭代

當(dāng)我們for...of時(shí),內(nèi)部會(huì)依次調(diào)用next方法進(jìn)行遍歷數(shù)據(jù)。因?yàn)槭堑?,每次next的值返回的就是yield的值,并且返回{value: xxx, done: false},直到最后{value: undefined, done: true}

總結(jié)

迭代器是一個(gè)對(duì)象,迭代器對(duì)象有一個(gè)next方法,當(dāng)我們調(diào)用next方法時(shí),會(huì)返回一個(gè)對(duì)象{value: xx, done: false},value就是當(dāng)前迭代器迭代的具體值,當(dāng)?shù)鲗?duì)象每調(diào)用一次next方法時(shí),就會(huì)獲取當(dāng)前的值,直到迭代完全,最后返回{done: true, value: undefined}

每一個(gè)迭代器都可以被for...of、數(shù)組解構(gòu)以及數(shù)組擴(kuò)展

生成器函數(shù),yield可以中斷函數(shù),當(dāng)我們調(diào)用函數(shù)生成器時(shí),實(shí)際上并不會(huì)立即執(zhí)行生成器函數(shù),當(dāng)這個(gè)調(diào)用的函數(shù)生成器在調(diào)用時(shí)會(huì)返回一個(gè)迭代器,每次調(diào)用next方法會(huì)返回一個(gè)對(duì)象,這個(gè)對(duì)象的值跟迭代器一樣,并且返回的value是yield的值,每次調(diào)用,才會(huì)執(zhí)行yield,后面的代碼會(huì)中斷。只有繼續(xù)調(diào)用next才會(huì)繼續(xù)往后執(zhí)行。

生成器函數(shù)調(diào)用返回的是一個(gè)迭代器,具備迭代器所有特性,yield這個(gè)狀態(tài)機(jī)只能在生成器函數(shù)內(nèi)部使用

以實(shí)際例子對(duì)對(duì)象擴(kuò)展支持迭代器特性,如果需要支持迭代器特征,那么必須原型上擴(kuò)展Symbol.iterator方法,以$myflat在數(shù)組原型上利用函數(shù)生成器實(shí)現(xiàn)扁平化數(shù)組等。

本文示例code-example[2]






作者:Maic


歡迎關(guān)注微信公眾號(hào) :web技術(shù)學(xué)苑