国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Generator函數的語法以及異步的應用

plokmju88 / 2416人閱讀

摘要:返回的遍歷器對象,可以依次遍歷函數內部的每一個狀態。由于函數就是遍歷器生成函數,因此可以把賦值給對象的屬性,從而使得該對象具有接口。函數從暫停狀態恢復運行,它的上下文狀態時不變的。從語義上講,第一個方法用來啟動遍歷器對象,所以不用帶有參數。

基本概念

Generator函數是ES6提供的一種異步編程解決方案,語法行為與傳統函數完全不同。Generator函數有多種理解角度。語法上,首先可以把它理解成,Generator函數是一個狀態機,封裝了多個內部狀態。
執行Generator函數會返回一個遍歷器對象,也就是說,Generator函數除了狀態機,還是一個遍歷器對象生成函數。返回的遍歷器對象,可以依次遍歷Generator函數內部的每一個狀態。
形式上,Generator函數是一個普通函數,但是有兩個特征。一是,function關鍵字與函數名之間有一個星號;二是,函數體內部使用yield表達式,定義不同的內部狀態(yield在英語里的意思就是產出)。

yield表達式解釋

1.語法。
[rv] = yield [expression];
expression:定義通過迭代器協議從生成器函數返回的值。如果省略,則返回undefined。
rv:返回傳遞給生成器的next()方法的可選值,以恢復其執行。
2.描述
yield關鍵字使生成器函數執行暫停,yield關鍵字后面的表達式的值返回給生成器的調用者。它可以被認為是一個基于生成器的版本return關鍵字。
yield關鍵字實際返回一個IteratorResult對象,它有兩個屬性,value和done。value屬性是對yield表達式求值的結果,而done是false,表示生成器函數尚未完全完成。

*yield,導致生成器再次暫停并返回生成器的新值。下一次調用next()時,在yield之后緊接著的語句繼續執行。
*throw用于從生成器中拋出異常。這讓生成器完全停止執行,并在調用者中繼續執行,正如通常情況下拋出異常一樣。
*到達生成器函數結尾;在這種情況下,生成器的執行結束,并且IteratorResult給調用者返回undefined并且done為true。
*到達return語句。這種情況下,生成器的執行結束,并將IterratorResult返回給調用者,其值是由return語句指定的,并且done為true。

如果將可選值傳遞給生成器的next()方法,則該值將稱為生成器當前yield操作返回的值。
在生成器的代碼路徑中yield運算符,以及通過將其傳遞給Generator.prototype.next()指定新的起始值的能力之間,生成器提供了強大的控制力。
3.實例.

function*countAppleSales(){
    var arr = [1,2,3];
    for (var i=0;i

Generator函數可以不用yield表達式,這時就變成了一個單純的暫緩執行函數。另外需要注意,yield表達式只能用在Generator函數里面,用在其它地方都會報錯。

function*f(){
    console.log("執行了!");
}
var generator = f();
generator.next();
//上面代碼中,函數f如果是普通函數,在為變量generator賦值時就會執行。但是,函數f是一個Generator函數,就變成只有調用next方法時才會執行。

下面是另一個例子:

var arr=[1,[[2,3],4],[5,6]];
var flat = function*(a){
    a.forEach(function(item){
        if(typeof item!=="niumber"){
            yield*flat(item);
        }else{
            yield item;
        }
    });
}
for (var f of flat(arr)){
    console.log(f);
}

上面代碼也會產生語句錯誤,因為forEach方法的參數是一個普通函數,但是在里面使用了yield表達式。一種修改方法是改用for循環。

var arr=[1,[[2,3],4],[5,6]];
var flat = function*(a){
    var length = a.length;
    for(var i=0;i

另外,yield表達式如果用在另一個表達式中,必須放在圓括號里面。

function*demo(){
    console.log("Hellow"+yield);//SyntaxError
    console.log("Hellow"+yield 123);//SyntaxError
    console.log("Hellow"+(yield));//ok
    console.log("Hellow"+(yield 123));//ok
}

yield表達式用作函數參數或放在賦值表達式的右邊,可以不加括號。

function*demo(){
    foo(yield "a",yield "b");//ok
    let input = yield;//ok
}
與Iterator接口的關系

任意一個對象的symbol.iterator方法,等于該對象的遍歷器生成函數,調用該函數會返回一個遍歷器對象。由于Generator函數就是遍歷器生成函數,因此可以把Generator賦值給對象的symbol.iterator屬性,從而使得該對象具有Iterator接口。

var myIterator = {};
myIterator[Symbol.iterator] = function*(){
    yield 1;
    yield 2;
    yield 3;
}
[...myIterator]//[1,2,3]

上面代碼中,Generator函數賦值給Symbol.iterator屬性,從而使得myIterable對象具有了Iterator接口,可以被...運算遍歷了。
Generator函數執行后,返回一個遍歷器對對象。該對象本身也具有Symbol.iterator屬性,執行后返回自身。

function* gen(){
  // some code
}
var g = gen();
g[Symbol.iterator]() === g
// true

上面代碼中,gen是一個Generator函數,調用它會生成一個遍歷器對象g。它的Symbol.iterator屬性,也是一個遍歷器對象生成函數,執行后返回它自己。

next方法的參數

yield表達式本身沒有返回值,或者說總是返回undefined。next方法可以帶一個參數,該參數就會被當作上一個yield表達式的返回值。

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

上面代碼先定義了一個可以無限運動的Generator函數f,如果next方法沒有參數,每次運行到yield表達式,變量reset的值總是undefined。當next方法帶一個參數true時,變量reset就被重置為這個參數(即true),因此i會等于-1,下一輪循環就會從-1開始遞增。
這個功能有很重要的語法意義。Generator函數從暫停狀態恢復運行,它的上下文狀態時不變的。通過next方法的參數,就有辦法在Generator函數開始運行之后,繼續向函數內部注入值。也就是說,可以在Generator函數運行的不同階段,從外部向內部注入不同的值,從而調整函數行為。
再看一個例子。

function*foo(x){
    var y = 2*(yield(x+1));
    var z = yield(y/3);
    return (x+y+z);
}
// var a = foo(5);
// console.log(a.next());//{value:6,done:false};
// console.log(a.next());//{value:NaN,done:false}因為此時y是undefined
// console.log(a.next());//{value:NaN,done:true}


var b = foo(5);
console.log(b.next());//{value:6,done:false};
console.log(b.next(12));//{value:8,done:false} 此時y=2*第一個yield的返回值(12)。
console.log(b.next(13));//z=13,y=24,x=5.{ value:42, done:true }

上面代碼中,第二次運行next方法的時候不帶參數,導致y的值等于2*undefined(即NaN),除以3以后還是NaN,因此返回對象的value屬性也等于NaN。第三次運行next方法的時候不帶參數,所以z等于undefined,返回對象的value屬性等于5+NaN+undefined,即NaN。
如果向next方法提供參數,返回結果就完全不一樣了。上面代碼第一次調用b的next方法時,返回x+1=6;第二次調用next方法,將上一次yield表達式的值設為12,因此y等于24,返回y/3的值8;第三次調用next方法,將上一次yield表達式的值設為13,因此z等于13,這時x等于5,y等于24,所以return語句的值等于42.
注意:由于next方法的參數表示上一個yield表達式的返回值,所以在第一次使用next方法時,傳遞參數是無效的。V8引擎直接忽略第一次使用next方法時的參數,只有從第二次使用next方法開始,參數才是有效的。從語義上講,第一個next方法用來啟動遍歷器對象,所以不用帶有參數。
再看一個通過next方法的參數,向Generator函數內部輸入值的例子。

function*dataConsumer(){
    console.log("Started");
    console.log(`1. ${yield}`);
    console.log(`2. ${yield}`);
    return "result";
}
let genObj = dataConsumer();
function* dataConsumer(){
  console.log("Started");
  console.log(`1. ${yield}`);
  console.log(`2. ${yield}`);
  return "result";
}
alet genObj = dataConsumer();
genObj.next();
//Started
genObj.next("a");
//1.a
genObj.next("b");
//2.b

上面代碼是一個很直觀的例子,每次通過next方法向Generator函數輸入值,然后打印出來。
如果想要第一次調用next方法時,就能夠輸入值,可以在Generator函數外面再包一層。

function wrapper(generatorFunction){
  return function(){
    let generatorObject = generatorFunction();
    generatorObject.next();
    return generatorObject;
  };
}
const wrapped = wrapper(function*(){
  console.log(`First input: ${yield}`);
  return "DONE";
});
wrapped().next("hello!");
// console.log(wrapped().next("hello!"))

上面代碼中,Generator函數如果不用wrapper先包一層,是無法第一次調用next方法,就輸入參數的。

for...of循環

for...of循環可以自動遍歷Generator函數時生成Iterator對象,且此時不再需要調用next方法。

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

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

上面代碼使用for...of循環,依次顯示5個yield表達式的值。這里需要注意,一旦next方法的返回對象的done屬性為true,for...of循環就會中止,且不包含該返回對象,所以上面代碼的return語句返回的6,不包括在for...of循環中。
下面是一個利用Generator函數for...of循環,實現斐波那契數列的例子。

function*fibonacci(){
    let [prev,curr] = [0,1];
    for(;;){
        yield curr;
        [prev,curr] = [curr,prev+curr];
    }
}
for(let n of fibonacci){
    if(n>100)break;
    console.log(n);
}

利用for...of循環,可以寫出遍歷任意對象的方法。原生JavaScript對象沒有遍歷接口,無法使用for...of循環,通過Gneerator函數為它加上這個接口,就可以用了。

//Reflect.ownKeys()方法返回target對象自己的屬性鍵的數組。
//Reflect.ownKeys({z:3,y:2,x:1});//["z","y","x"];
//Reflect.ownKeys([]);//["length"];
//Reflect.ownKeys([1,2,3,4]);//["1","2","3","4","length"]
function*objectEntries(obj){
  let propKeys = Reflect.ownKeys(obj);
  for(let propKey of propKeys){
    yield [propKey,obj[propKey]]
  }
}
let jane = {first:"jane",last:"Doe"};
for(let[key,value] of objectEntries(jane)){
  console.log(`${key}:${value}`);
}
// first: Jane
// last: Doe

上面代碼中,對象jane原生不具備 Iterator 接口,無法用for...of遍歷。這時,我們通過 Generator 函數objectEntries為它加上遍歷器接口,就可以用for...of遍歷了。加上遍歷器接口的另一種寫法是,將 Generator 函數加到對象的Symbol.iterator屬性上面。

//Object.keys 返回一個所有元素為字符串的數組,其元素來自于從給定的object上面可直接枚舉的屬性。這些屬性的順序與手動遍歷該對象屬性時的一致。
// simple array
var arr = ["a", "b", "c"];
console.log(Object.keys(arr)); // console: ["0", "1", "2"]

// array like object
var obj = { 0: "a", 1: "b", 2: "c" };
console.log(Object.keys(obj)); // console: ["0", "1", "2"]

// array like object with random key ordering
var anObj = { 100: "a", 2: "b", 7: "c" };
console.log(Object.keys(anObj)); // console: ["2", "7", "100"]

// getFoo is a property which isn"t enumerable
var myObj = Object.create({}, {
  getFoo: {
    value: function () { return this.foo; }
  } 
});
myObj.foo = 1;
console.log(Object.keys(myObj)); // console: ["foo"]

/**/
function* objectEntries() {
  let propKeys = Object.keys(this);
  for (let propKey of propKeys) {
    yield [propKey, this[propKey]];
  }
}

let jane = { first: "Jane", last: "Doe" };
jane[Symbol.iterator] = objectEntries;

for (let [key, value] of jane) {
  console.log(`${key}: ${value}`);
}
// first: Jane
// last: Doe

除了for...of循環以外,擴展運算符(...)、解構賦值和Array.from方法內部調用的,都是遍歷器接口。這意味著,它們都可以將 Generator 函數返回的 Iterator 對象,作為參數。

function* numbers () {
  yield 1
  yield 2
  return 3
  yield 4
}

// 擴展運算符
[...numbers()] // [1, 2]

// Array.from 方法
Array.from(numbers()) // [1, 2]

// 解構賦值
let [x, y] = numbers();
x // 1
y // 2

// for...of 循環
for (let n of numbers()) {
  console.log(n)
}
// 1
// 2

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/99505.html

相關文章

  • 《Node.js設計模式》基于ES2015+回調控制流

    摘要:以下展示它是如何工作的函數使用構造函數創建一個新的對象,并立即將其返回給調用者。在傳遞給構造函數的函數中,我們確保傳遞給,這是一個特殊的回調函數。 本系列文章為《Node.js Design Patterns Second Edition》的原文翻譯和讀書筆記,在GitHub連載更新,同步翻譯版鏈接。 歡迎關注我的專欄,之后的博文將在專欄同步: Encounter的掘金專欄 知乎專欄...

    LiuRhoRamen 評論0 收藏0
  • ES6 - Generator函數

    摘要:最后,調用這個函數,得到一個遍歷器對象并賦值給變量。值得注意的是如果函數內部沒有部署代碼塊,那么遍歷器對象的方法拋出的錯誤,將被外部代碼塊捕獲。 本文已同步至我的個人主頁。歡迎訪問查看更多內容!如有錯誤或遺漏,歡迎隨時指正探討!謝謝大家的關注與支持! 一、什么是Generator函數 Generator函數是ES6標準中提出的一種異步編程的解決方案。這種函數與普通函數最大的區別在于它可...

    Ali_ 評論0 收藏0
  • 深入理解Generator

    摘要:如果你已經理解基礎可以直接跳過這部分和語法部分,直接看深入理解的部分。的參數可以傳入一個參數,來作為上一次的表達式的返回值,就像我們上面說的會讓等于。 這篇文章旨在幫你真正了解Generator,文章較長,不過如果能花時間耐心看完,相信你已經能夠完全理解generator 為什么要用generator 在前端開發過程中我們經常需要先請求后端的數據,再用拿來的數據進行使用網頁頁面渲染等操...

    Euphoria 評論0 收藏0
  • ES6-7

    摘要:的翻譯文檔由的維護很多人說,阮老師已經有一本關于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...

    mudiyouyou 評論0 收藏0
  • JavaScript 異步

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。寫一個符合規范并可配合使用的寫一個符合規范并可配合使用的理解的工作原理采用回調函數來處理異步編程。 JavaScript怎么使用循環代替(異步)遞歸 問題描述 在開發過程中,遇到一個需求:在系統初始化時通過http獲取一個第三方服務器端的列表,第三方服務器提供了一個接口,可通過...

    tuniutech 評論0 收藏0

發表評論

0條評論

plokmju88

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<