一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

代碼寫了那么多,你搞明白yield是個啥沒?

 八塊腹肌的壞蛋 2016-11-07

本文作者:imweb 否子戈 原文出處:imweb社區(qū)

ES6真是顛覆JavaScript的東西,隨便翻一個新特性出來,就讓自以為是的老古董們傻眼跳樓。在之前接觸ember.js的時候,接觸到了yield,嫩是半天沒明白,yield到底是什么,想要實現什么目的。后來在看ES6的東西的時候,總算好像知道了一點,迫不及待的寫出來。

在MDN上,對yield的第一句解釋就是:

The yield keyword is used to pause and resume a generator function.
// yield這個關鍵字是用來暫停和恢復一個遍歷器函數(的運行)的。

這也就是yield的所有解釋了,可謂大道至簡,然并卵,深層的意思不去挖掘,根本還是沒法用它,還是老老實實做老古董。

關鍵字yield

沒錯,yield是個關鍵字,不是函數。關鍵字用來干啥?它的作用是“命令”。和var不同,不是用來聲明,但是和return一樣,用來告知程序某種狀態(tài),return告訴程序要返回什么值(也意味著結束,結束的時候才會返回值嘛),而yield告訴程序當前的狀態(tài)值,而且你運行到這里給我暫停一下。

因為yield是命令型的關鍵字,所以它的用法是:

[rv] = yield [expression];

rv是可選的,這里不是說它返回一個數組。yield后面的表達式也是可選的。yield的返回值是一個狀態(tài)值。如果從返回值的角度講,yield還可以當做是一種運算符,但是由于它的作用是暫停和恢復,所以嚴格意義上說,不能叫運算符,運算符是用來運算的,而yield是用來“命令”的。

遍歷器函數(Generator)

這是ES6里面新增的Generator,為了把它和我們已知的東西聯系起來,我把它翻譯為遍歷器函數,但是實際上現在還沒有統一的叫法,坐等大神們開宗立派。

我個人的理解,Generator函數的最大用處就是用來生成一個遍歷器。

不過它是一個函數,所以和普通的函數有點區(qū)別,因此在聲明函數的時候,要在function和函數名之間加一個*號:

function *foo() {}

而yield也必須在Generator函數中才有意義,脫離了Generator就沒意義了啊,這就像說“鯊魚是用來在海里面消滅傻瓜魚的”現在你吧鯊魚丟到陸地上,請問它有啥意義?給人做包包嗎?

Generator函數的一個重要特點就是需要執(zhí)行next()方法才能運行,聲明好它之后,根本不會馬上運行。舉個栗子:

var a = 0;
function *foo() {
  a += 1;
  yield '';
  return;
}
var f = foo();
alert(a); // 這個時候是啥值?

上面的例子如此簡單,你不會看不懂吧?現在告訴你,alert的結果是0!

我前面都說了,要執(zhí)行netx()方法才會運行。所以在上面的代碼末尾添加:

f.next();
alert(a); // 這個時候就會alert(1)了

沒怎明白?繼續(xù)往下讀

.next()方法

就用上面的代碼來說好了。Generator函數的特點就是“它是一個遍歷器”,你不讓它動,它絕對不會動。

不要用“動”這么猥瑣的詞好嗎?

“執(zhí)行、運行”,當調用f.next()的時候,程序從f這個遍歷器函數的開始運行,當遇到yield命令時,表示“暫停”,并且返回yield [expression]的狀態(tài)。比如程序在往下運行的時候,遇到yield 1 + 2,那么next()返回的結果就是這個時候的狀態(tài)。

而f.next()就是讓它往下一個元素遍歷的動作,它的返回值其實表示一個狀態(tài),是一個object:{value: xxx, done: false}。value表示yield后面的表達式的結果值,done表示是否已經遍歷完。把上面的f.next()那段代碼改成下面的:

var s1 = f.next(); 
console.log(s1); // {value: '',done: false}
var s2 = f.next();
console.log(s2); // {value: undefined,done: true}

第一個console的地方,因為第一次執(zhí)行f.next(),遇到yield就暫停了,得到s1這個狀態(tài),這個狀態(tài)的value是yield后面緊跟的表達式的值,done表示遍歷沒有結束,還可以繼續(xù)執(zhí)行next()方法。第二次再執(zhí)行f.next()的時候,遇到了return,但后面沒有表達式,所以返回值是undefined,一旦遇到return就表示遍歷可以結束了,所以done為true。

當運行到yield '';的時候,程序暫停了,不會往下繼續(xù)執(zhí)行,所以下面的各種加減乘除都不會運行,這也就是為什么我們上面的代碼在運行f.next()之前,雖然執(zhí)行了foo()這個函數,但是a的值是0,就是因為還沒有執(zhí)行f.next(),所以yield前面的a += 1還沒有運行。

也正是因為有.next()方法,所以它叫遍歷器。for...of,...,Array.from都是利用遍歷器接口進行運算的,所以如果沒有Generator,這幾個方法就用不了。而在Babel里面,babel-core本身默認不支持Generator,所以這幾個運算其實也需要另外的模塊( babel-polyfill )來支持。

一不小心講到了babel,罪過罪過。

.next()的參數

造孽的地方終于要來了。一言不合,就上代碼:

var foo = function *() { // 沒錯,尼瑪還可以這樣寫
  var x = 1;
  var y =  yield (x + 1);
  var z = yield (x + y);
  return z;
}() // 你必須先執(zhí)行一下Generator函數,才能把遍歷器返回給某個變量
var a = foo.next(); // 第一次執(zhí)行next()不可以傳參
var b = foo.next(3);
var c = foo.next(4);

求abc的value各是多少?

你要沒接觸過,這個時候只會冒出來“吊雷老某”……a.value你應該知道,就2(x = 1, x + 1 = 2)。b.value呢?傻戳戳了吧。

.next()的參數的意思是將傳入的參數用作上一次的yield。啥子意思?就是第二次執(zhí)行foo.next(3)的時候,yield x + 1這一大坨就是3,所以y = 3!“xx老謀”。所以,b.value的結果就是4 (x = 1,y = 3, x + y = 4).

這樣推下去咯。最后返回的是z,但是傳入的是4,yield (x + y) 這一大坨就用4來代替,z.value = yield (x + y) = 4。

兩個問題:1. 為什么第一次執(zhí)行next()不能傳參?2.為什么yield后面要加括號?

第一個問題,因為第一次執(zhí)行next()的時候,你傳入的參數沒法去代替某一個yield??!

第二個問題,其實可以不用加括號,但是如果不加括號的話,那么后面的+運算就沒了,比如說上面的代碼改為:

var foo = function *() {
  var x = 1;
  var y =  yield (x + 1);
  var z = yield x + y;
  return z;
}()
var a = foo.next();
var b = foo.next(3);
var c = foo.next(4);

這差距就大了,yield x是一坨,不會等到你x+y它就會返回,所以b還是1.

把yield當變量看

上面這一個小節(jié)你有沒有發(fā)現一個神奇的規(guī)律?就是你在給next()傳參的時候,總是對應某一個yield,把這個yield以及后面緊跟著的表達式用傳入的參數代替。于是乎,下面這個代碼你就懂了:

var foo = function *() {
  var x = 1;
  var y =  yield (x + 1);
  var z = yield;
  return z;
}()
var a = foo.next();
var b = foo.next(3);
var c = foo.next(4);

上面這個代碼里面,竟然把yield后面的表達式給去掉了。那會有啥影響?b.value將等于undefined,僅此而已。因為b這個位置就是得到yield [空]的結果,所以就是undefined咯。但是當foo.next(4)執(zhí)行的時候,不管你上面是不是undefined,我現在就是要用4來覆蓋你,所以z.value還是4.

也正因為這樣,在字符串里面,可以這樣使用:

var log = function *() {
  console.log(`you input: ${yeild}`)
}().next(); // 這里會提示錯誤: yeild undefined
log.next('hello world!');

上面這個例子把yield當做一個變量來使用,如果你感興趣,可以想辦法吧第一次的時候錯誤去除掉。

這個用法看上去很挫,但是很厲害的是,可以通過這個思路,實現字符串模板的次第渲染??梢钥刂其秩镜侥膫€位置。

總結一下

寫了這么多,總結一下yield,實際上:

  • 只能在Generator函數內部使用
  • 運行.next(),遇到一個yield命令,就暫停
  • .next()的返回值表示一個狀態(tài){value,done}
  • 再運行.next(),從之前遇到的那個yield [表達式]處(后)繼續(xù)恢復運行
  • 當.next()傳參的時候,yield [表達式]整個被替換為傳入的參數。

最后,說一下for...of是怎么運行的。

function *foo() {
  yield 1;
  yield 2;
  yield 3;
  return;
}
for(let v of foo()) {
  console.log(v);
}

for...of在遍歷foo()返回的結果時,每遇到一個yield,就把yield后面表達式的結果作為of前面的v的值進行賦值(next()返回值的value字段)。沒錯,就這么不要臉的解釋完了。

最后的最后,這篇文章不是為了解釋和學習Generator的,所以要了解更多Generator,請繼續(xù)關注我的博客:)

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    在线中文字幕亚洲欧美一区| 少妇一区二区三区精品| 国产肥女老熟女激情视频一区| 久久精品偷拍视频观看| 在线视频免费看你懂的| 精品日韩欧美一区久久| 亚洲做性视频在线播放| 久久精品伊人一区二区| 欧美日韩一区二区三区色拉拉| 中文字幕中文字幕在线十八区| 色好吊视频这里只有精| 国产精品一区二区三区黄色片| 日韩欧美三级视频在线| 欧美日韩有码一二三区| 日韩成人中文字幕在线一区 | 欧美中文字幕日韩精品| 久久精品色妇熟妇丰满人妻91| 又黄又色又爽又免费的视频| 国产日韩在线一二三区| 亚洲男人天堂成人在线视频| 亚洲国产av在线视频| 欧美亚洲综合另类色妞| 99国产高清不卡视频| 99久久国产精品免费| 一区二区三区日韩经典| 日韩黄片大全免费在线看| 欧美国产亚洲一区二区三区| 国产精品亚洲综合色区韩国| 大伊香蕉一区二区三区| 欧美丰满大屁股一区二区三区| 久久精品国产亚洲熟女| 精品熟女少妇av免费久久野外| 东京热男人的天堂久久综合| 欧美黑人暴力猛交精品| 久久久精品日韩欧美丰满| 欧美日韩校园春色激情偷拍| 草草视频福利在线观看| 国内女人精品一区二区三区| 日韩人妻有码一区二区| 91精品视频免费播放| 久久久精品区二区三区|