成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

ES6 Generator 基礎(chǔ)指南

BlackHole1 / 3050人閱讀

摘要:這一概念和相對(duì),認(rèn)為可以在進(jìn)程函數(shù)外部對(duì)其終止運(yùn)行。對(duì)于從其他語言轉(zhuǎn)向的人來說,它看起來很像函數(shù)返回值指針。

本文翻譯自:The Basics Of ES6 Generators

由于個(gè)人能力有限,翻譯中難免有紕漏和錯(cuò)誤,望不吝指正issue

JavaScript ES6(譯者注:ECMAScript 2015)中最令人興奮的特性之一莫過于Generator函數(shù),它是一種全新的函數(shù)類型。它的名字有些奇怪,初見其功能時(shí)甚至更會(huì)有些陌生。本篇文章旨在解釋其基本工作原理,并幫助你理解為什么Generator將在未來JS中發(fā)揮強(qiáng)大作用。

Generator從運(yùn)行到完成的工作方式

但我們談?wù)揋enerator函數(shù)時(shí),我們首先應(yīng)該注意到的是,從“運(yùn)行到完成”其和普通的函數(shù)表現(xiàn)有什么不同之處。

不論你是否已經(jīng)意識(shí)到,你已經(jīng)潛意識(shí)得認(rèn)為函數(shù)具有一些非?;A(chǔ)的特性:函數(shù)一旦開始執(zhí)行,那么在其結(jié)束之前,不會(huì)執(zhí)行其他JavaScript代碼。

例如:

setTimeout(function(){
    console.log("Hello World");
},1);

function foo() {
    // NOTE: don"t ever do crazy long-running loops like this
    for (var i=0; i<=1E10; i++) {
        console.log(i);
    }
}

foo();
// 0..1E10
// "Hello World"

上面的代碼中,for循環(huán)會(huì)執(zhí)行相當(dāng)長(zhǎng)的時(shí)間,長(zhǎng)于1秒鐘,但是在foo()函數(shù)執(zhí)行的過程中,我們帶有console.log(...)的定時(shí)器并不能夠中斷foo()函數(shù)的運(yùn)行。因此代碼被阻塞,定時(shí)器被推入事件循環(huán)的最后,耐心等待foo函數(shù)執(zhí)行完成。

倘若foo()可以被中斷執(zhí)行?它不會(huì)給我們的帶來前所未有的浩劫嗎?

函數(shù)可以被中斷對(duì)于多線程編程來說確實(shí)是一個(gè)挑戰(zhàn),但是值得慶幸的是,在JavaScript的世界中我們沒必要為此而擔(dān)心,因?yàn)镴S總是單線程的(在任何時(shí)間只有一條命令/函數(shù)被執(zhí)行)。

注意: Web Workers是JavaScript中實(shí)現(xiàn)與JS主線程分離的獨(dú)立線程機(jī)制,總的說來,Web Workers是與JS主線程平行的另外一個(gè)線程。在這兒我們并不介紹多線程并發(fā)的一個(gè)原因是,主線程和Web Workers線程只能夠通過異步事件進(jìn)行通信,因此每個(gè)線程內(nèi)部從運(yùn)行到結(jié)束依然遵循一個(gè)接一個(gè)的事件循環(huán)機(jī)制。

運(yùn)行-停止-運(yùn)行

由于ES6Generators的到來,我們擁有了另外一種類型的函數(shù),這種函數(shù)可以在執(zhí)行的過程中暫停一次或多次,在將來的某個(gè)時(shí)間繼續(xù)執(zhí)行,并且允許在Generator函數(shù)暫停的過程中運(yùn)行其他代碼。

如果你曾經(jīng)閱讀過關(guān)于并發(fā)或者多線程編程的資料,那你一定熟悉“協(xié)程”這一概念,“協(xié)程”的意思就是一個(gè)進(jìn)程(就是一個(gè)函數(shù))其可以自行選擇終止運(yùn)行,以便可以和其他代碼“協(xié)作”完成一些功能。這一概念和“preemptive”相對(duì),preemptive認(rèn)為可以在進(jìn)程/函數(shù)外部對(duì)其終止運(yùn)行。

根據(jù)ES6 Generator函數(shù)的并發(fā)行為,我們可以認(rèn)為其是一種“協(xié)程”。在Generator函數(shù)體內(nèi)部,你可以使用yield關(guān)鍵字在函數(shù)內(nèi)部暫停函數(shù)的執(zhí)行,在Generator函數(shù)外部是無法暫停一個(gè)Generator函數(shù)執(zhí)行的;每當(dāng)Generator函數(shù)遇到一個(gè)yield關(guān)鍵字就將暫停執(zhí)行。

然后,一旦一個(gè)Generator函數(shù)通過yield暫停執(zhí)行,其不能夠自行恢復(fù)執(zhí)行,需要通過外部的控制來重新啟動(dòng)generator函數(shù),我們將在文章后面部分介紹這是怎么發(fā)生的。

基本上,只要你愿意,一個(gè)Generator函數(shù)可以暫停執(zhí)行/重新啟動(dòng)任意多次。實(shí)際上,你可以再Generator函數(shù)內(nèi)部使用無限循環(huán)(比如非著名的while (true) { .. })來使得函數(shù)可以無盡的暫停/重新啟動(dòng)。然后這在普通的JS程序中卻是瘋狂的行徑,甚至?xí)伋鲥e(cuò)誤。但是Generator函數(shù)卻能夠表現(xiàn)的非常明智,有些時(shí)候你確實(shí)想利用Generator函數(shù)這種無盡機(jī)制。

更為重要的是,暫停/重新啟動(dòng)不僅僅用于控制Generator函數(shù)執(zhí)行,它也可以在generator函數(shù)內(nèi)部和外部進(jìn)行雙向的通信。在普通的JavaScript函數(shù)中,你可以通過傳參的形式將數(shù)據(jù)傳入函數(shù)內(nèi)容,在函數(shù)內(nèi)部通過return語句將函數(shù)的返回值傳遞到函數(shù)外部。在generator函數(shù)中,我們通過yield表達(dá)式將信息傳遞到外部,然后通過每次重啟generator函數(shù)將其他信息傳遞給generator。

Generator 函數(shù)的語法

然我們看看新奇并且令人興奮的generator函數(shù)的語法是怎樣書寫的。

首先,新的函數(shù)聲明語法:

function *foo() {
    // ..
}

發(fā)現(xiàn)*符號(hào)沒?顯得有些陌生且有些奇怪。對(duì)于從其他語言轉(zhuǎn)向JavaScript的人來說,它看起來很像函數(shù)返回值指針。但是不要被迷惑到了,*只是用于標(biāo)識(shí)generator函數(shù)而已。

你可能會(huì)在其他的文章/文檔中看到如下形式書寫generator函數(shù)function* foo(){},而不是這樣function *foo() {}(*號(hào)的位置有所不同)。其實(shí)兩種形式都是合法的,但是最近我認(rèn)為后面一種形式更為準(zhǔn)確,因此在本篇文章中都是使用后面一種形式。

現(xiàn)在,讓我們來討論下generator函數(shù)的內(nèi)部構(gòu)成吧。在很多方面,generator函數(shù)和普通函數(shù)無異,只有在generator函數(shù)內(nèi)部有一些新的語法。

正如上面已經(jīng)提及,我們最先需要了解的就是yield關(guān)鍵字,yield__被視為“yield表達(dá)式”(并不是一條語句),因?yàn)楫?dāng)我們重新啟動(dòng)generator函數(shù)的時(shí)候,我們可以傳遞信息到generator函數(shù)內(nèi)部,不論我們傳遞什么進(jìn)去,都將被視為yield__表達(dá)式的運(yùn)行結(jié)果。

例如:

function *foo() {
    var x = 1 + (yield "foo");
    console.log(x);
}

yield "foo"表達(dá)式會(huì)在generator函數(shù)暫停時(shí)把“foo”字符串傳遞到外部。同時(shí),當(dāng)generator函數(shù)恢復(fù)執(zhí)行的時(shí)候,其他的值又會(huì)通過其他表達(dá)式傳入到函數(shù)里面作為yield表達(dá)式的返回值加1最后再將結(jié)果賦值給x變量。

看到generator函數(shù)的雙向通信了嗎?generator函數(shù)將‘’foo‘’字符串傳遞到外部,暫停函數(shù)執(zhí)行,在將來的某個(gè)時(shí)間點(diǎn)(可能是立即也可能是很長(zhǎng)一段時(shí)間后),generator會(huì)被重啟,并且會(huì)傳遞一個(gè)值給generator函數(shù),就好像yield關(guān)鍵字就是某種發(fā)送請(qǐng)求獲取值的請(qǐng)求形式。

在任意表達(dá)式中,你可以僅使用yield關(guān)鍵字,后面不跟任何表達(dá)式或值。在這種情況下,就相當(dāng)于將undefined通過yield傳遞出去。如下代碼:

// note: `foo(..)` here is NOT a generator!!
function foo(x) {
    console.log("x: " + x);
}

function *bar() {
    yield; // just pause
    foo( yield ); // pause waiting for a parameter to pass into `foo(..)`
}
Generator 迭代器

“Generator 迭代器”,是不是相當(dāng)晦澀難懂?

迭代器是一種特殊的行為,準(zhǔn)確說是一種設(shè)計(jì)模式,當(dāng)我們通過調(diào)用next()方法去遍歷一組值的集合時(shí),例如,我們通過在長(zhǎng)度為5的數(shù)組[1, 2, 3, 4, 5]上面實(shí)現(xiàn)了迭代器。當(dāng)我們第一次調(diào)用next()的時(shí)候,會(huì)返回1。第二次調(diào)用next()返回2,如此下去,當(dāng)所有的值都返回后,再次調(diào)用next()將返回null或者false或其他值,這意味著你已經(jīng)遍歷完真?zhèn)€數(shù)組中的值了。

我們是通過和generator迭代器進(jìn)行交互來在generator函數(shù)外部控制generator函數(shù),這聽起來比起實(shí)際上有些復(fù)雜,考慮下面這個(gè)愚蠢的(簡(jiǎn)單的)例子:

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

為了遍歷*foo()generator函數(shù)中的所有值,我們首先需要構(gòu)建一個(gè)迭代器,我們?cè)趺慈?gòu)建這個(gè)迭代器呢?非常簡(jiǎn)單!

var it = foo();

如此之簡(jiǎn)單,我們僅僅想執(zhí)行普通函數(shù)一樣執(zhí)行g(shù)enerator函數(shù),其將返回一個(gè)迭代器,但是generator函數(shù)中的代碼并不會(huì)運(yùn)行。

這似乎有些奇怪,并且增加了你的理解難度。你甚至?xí)O聛硭伎?,問為什么不通過var it = new foo()的形式來執(zhí)行g(shù)enerator函數(shù)呢,這語法后面的原因可能相當(dāng)復(fù)雜并超出了我們的討論范疇。

好的,現(xiàn)在讓我們開始迭代我們的generator函數(shù),如下:

var message = it.next();

通過上面的語句,yield表達(dá)式將1返回到函數(shù)外部,但是返回的值可能比想象中會(huì)多一些。

console.log(message); // { value:1, done:false }

在每一調(diào)用next()后,我們實(shí)際上從yield表達(dá)式的返回值中獲取到了一個(gè)對(duì)象,這個(gè)對(duì)象中有value字段,就是yield返回的值,同時(shí)還有一個(gè)布爾類型的done字段,其用來表示generator函數(shù)是否已經(jīng)執(zhí)行完畢。

然我們把迭代執(zhí)行完成。

console.log( it.next() ); // { value:2, done:false }
console.log( it.next() ); // { value:3, done:false }
console.log( it.next() ); // { value:4, done:false }
console.log( it.next() ); // { value:5, done:false }

有趣的是,當(dāng)我們獲取到值為5的時(shí)候,done字段依然是false。這因?yàn)椋瑢?shí)際上generator函數(shù)還么有執(zhí)行完全,我們還可以再次調(diào)用next()。如果我們向函數(shù)內(nèi)部傳遞一個(gè)值,其將被設(shè)置為yield 5表達(dá)式的返回值,只有在這時(shí)候,generator函數(shù)才執(zhí)行完全。

代碼如下:

console.log( it.next() ); // { value:undefined, done:true }

所以最終結(jié)果是,我們迭代執(zhí)行完我們的generator函數(shù),但是最終卻沒有結(jié)果(由于我們已經(jīng)執(zhí)行完所有的yield__表達(dá)式)。

你可能會(huì)想,我能不能在generator函數(shù)中使用return語句,如果我這樣這,返回值會(huì)不會(huì)在最終的value字段里面呢?

...

function *foo() {
    yield 1;
    return 2;
}

var it = foo();

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

... 不是.

依賴于generator函數(shù)的最終返回值也許并不是一個(gè)最佳實(shí)踐,因?yàn)楫?dāng)我們通過for--of循環(huán)來迭代generator函數(shù)的時(shí)候(如下),最終return的返回值將被丟棄(無視)。

為了完整,讓我們來看一個(gè)同時(shí)有雙向數(shù)據(jù)通信的generator函數(shù)的例子:

function *foo(x) {
    var y = 2 * (yield (x + 1));
    var z = yield (y / 3);
    return (x + y + z);
}

var it = foo( 5 );

// note: not sending anything into `next()` here
console.log( it.next() );       // { value:6, done:false }
console.log( it.next( 12 ) );   // { value:8, done:false }
console.log( it.next( 13 ) );   // { value:42, done:true }

你可以看到,我們依然可以通過foo(5)傳遞參數(shù)(在例子中是x)給generator函數(shù),就像普通函數(shù)一樣,是的參數(shù)x5.

在第一次執(zhí)行next(..)的時(shí)候,我們并沒有傳遞任何值,為什么?因?yàn)樵趃enerator內(nèi)部并沒有yield表達(dá)式來接收我們傳遞的值。

假如我們真的在第一次調(diào)用next(..)的時(shí)候傳遞了值進(jìn)去,也不會(huì)帶來什么壞處,它只是將這個(gè)傳入的值拋棄而已。ES6表明,generator函數(shù)在這種情況只是忽略了這些沒有被用到的值。(注意:在寫這篇文章的時(shí)候,Chrome和FF的每夜版支持這一特性,但是其他瀏覽有可能沒有完全支持這一特性甚至可能會(huì)拋出錯(cuò)誤)(譯者注:文章發(fā)布于2014年)

yield(x + 1)表達(dá)式將傳遞值6到外部,在第二次調(diào)用next(12)時(shí)候,傳遞12到generator函數(shù)內(nèi)部作為yield(x + 1)表達(dá)式的值,因此y被賦值為12 * 2,值為24。接下來,下一條yield(y / 3)(yield (24 / 3))將向外傳遞值8。第三次調(diào)用next(13)傳遞13到generator函數(shù)內(nèi)部,給yield(y / 3)。是的z被設(shè)置為13.

最后,return (x + y + z)就是return (5 + 24 + 13),也就是42將會(huì)作為最終的值返回出去。

重新閱讀幾遍上面的實(shí)例。最開始有些難以理解。

for..of循環(huán)

ES6在語法層面上大力擁抱迭代器模式,提供了for..of循環(huán)來直接支持迭代器的遍歷。

例如:

function *foo() {
    yield 1;
    yield 2;
    yield 3;
    yield 4;
    yield 5;
    return 6;
}

for (var v of foo()) {
    console.log( v );
}
// 1 2 3 4 5

console.log( v ); // still `5`, not `6` :(

正如你所見,通過調(diào)用foo()生成的迭代器通過for..of循環(huán)來迭代,循環(huán)自動(dòng)幫你對(duì)迭代器進(jìn)行遍歷迭代,每次迭代返回一個(gè)值,直到done: true,只要done: false,每次循環(huán)都將從value屬性上獲取到值賦值給迭代的變量(例子中的v)。一旦當(dāng)donetrue。循環(huán)迭代結(jié)束。(for..of循環(huán)不會(huì)對(duì)generator函數(shù)最終的return值進(jìn)行處理)

正如你所看到的,for..of循環(huán)忽略了generator最后的return 6的值,同時(shí),循環(huán)沒有暴露next()出來,因此我們也不能夠向generator函數(shù)內(nèi)傳遞數(shù)據(jù)。

總結(jié)

OK,上面是關(guān)于generator函數(shù)的基本用法,如果你依然對(duì)generator函數(shù)感到費(fèi)解,不要擔(dān)心,我們所有人在一開始感覺都是那樣的。

我們很自然的想到這一外來的語法對(duì)我們實(shí)際代碼有什么作用呢?generator函數(shù)有很多作用,我們只是挖掘了其非常粗淺的一部分。在我們發(fā)現(xiàn)generator函數(shù)如此強(qiáng)大之前我們應(yīng)該更加深入的了解它。

在你練習(xí)上面代碼片段之后(在Chrome或者FF每夜版本,或者0.11+帶有--harmony的node環(huán)境下),下面的問題也許會(huì)浮出水面:(譯者注:現(xiàn)代瀏覽器最新版本都已支持Generator函數(shù))

怎樣處理generator內(nèi)部錯(cuò)誤?

在generator函數(shù)內(nèi)部怎么調(diào)用其他generator函數(shù)?

異步代碼怎么和generator函數(shù)協(xié)同工作?

這些問題,或者其他的問題都將在隨后的文章中覆蓋,敬請(qǐng)期待。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/84692.html

相關(guān)文章

  • [譯]JavaScript ES6迭代器指南

    摘要:前言又稱提供一個(gè)全新的迭代器的概念,它允許我們?cè)谡Z言層面上定義一個(gè)有限或無限的序列。后者可以被用來幫助我們理解迭代器。但是當(dāng)我們使用迭代器時(shí),這個(gè)問題就迎刃而解了。是中的新語法,用來配合迭代器。這是因?yàn)閿?shù)組的迭代器只返回其中預(yù)期的元素。 前言 EcmaScript 2015 (又稱ES6)提供一個(gè)全新的迭代器的概念,它允許我們?cè)谡Z言層面上定義一個(gè)(有限或無限的)序列。 暫時(shí)先拋開它...

    daryl 評(píng)論0 收藏0
  • 深入理解 Generator 函數(shù)

    摘要:同時(shí),迭代器有一個(gè)方法來向函數(shù)中暫停處拋出一個(gè)錯(cuò)誤,該錯(cuò)誤依然可以通過函數(shù)內(nèi)部的模塊進(jìn)行捕獲處理。 本文翻譯自:Diving Deeper With ES6 Generators 由于個(gè)人能力有限,翻譯中難免有紕漏和錯(cuò)誤,望不吝指正issue ES6 Generators:完整系列 The Basics Of ES6 Generators Diving Deeper With E...

    jzzlee 評(píng)論0 收藏0
  • [譯] 從 CoffeeScript 遷移到 ES6

    摘要:語法校驗(yàn)會(huì)給出警告當(dāng)你仍在使用或不通過任何關(guān)鍵字聲明變量時(shí)。但是如果腳本中還有其他的普通導(dǎo)出,就會(huì)得到非常奇怪的結(jié)果這個(gè)坑爹的情況目前還沒有任何好的解決方案。 我在多年前愛上了coffeScript。對(duì)于javaScript,我一直保持著深沉的愛,也十分高興得看到node.js的快速發(fā)展,但是作為一個(gè)有python背景的程序員,我更喜歡coffeeScript的簡(jiǎn)練語法。 在任何一個(gè)活...

    劉東 評(píng)論0 收藏0
  • 前端練級(jí)攻略(第二部分)

    摘要:是文檔的一種表示結(jié)構(gòu)。這些任務(wù)大部分都是基于它。這個(gè)實(shí)踐的重點(diǎn)是把你在前端練級(jí)攻略第部分中學(xué)到的一些東西和結(jié)合起來。一旦你進(jìn)入框架部分,你將更好地理解并使用它們。到目前為止,你一直在使用進(jìn)行操作。它是在前端系統(tǒng)像今天這樣復(fù)雜之前編寫的。 本文是 前端練級(jí)攻略 第二部分,第一部分請(qǐng)看下面: 前端練級(jí)攻略(第一部分) 在第二部分,我們將重點(diǎn)學(xué)習(xí) JavaScript 作為一種獨(dú)立的語言,如...

    BWrong 評(píng)論0 收藏0
  • 2017-08-20 前端日?qǐng)?bào)

    摘要:前端日?qǐng)?bào)精選數(shù)組所有全解密原生實(shí)現(xiàn)最簡(jiǎn)單的圖片懶加載譯如何抓取數(shù)據(jù)中種常見的內(nèi)存泄露陷阱內(nèi)部原理,第一部分基礎(chǔ)渲染前端國(guó)際化中文深入理解筆記模塊掘金譯熱的冷的掘金模塊,桌面端的支付請(qǐng)求,和迷津欲有問遮罩層狀態(tài)丟失及解決方案全 2017-08-20 前端日?qǐng)?bào) 精選 JavaScript數(shù)組所有API全解密原生JS實(shí)現(xiàn)最簡(jiǎn)單的圖片懶加載【譯】React如何抓取數(shù)據(jù)JavaScript 中 ...

    molyzzx 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<