ES6真是顛覆JavaScript的東西,隨便翻一個新特性出來,就讓自以為是的老古董們傻眼跳樓。在之前接觸ember.js的時候,接觸到了yield,嫩是半天沒明白,yield到底是什么,想要實現什么目的。后來在看ES6的東西的時候,總算好像知道了一點,迫不及待的寫出來。 在MDN上,對yield的第一句解釋就是:
這也就是yield的所有解釋了,可謂大道至簡,然并卵,深層的意思不去挖掘,根本還是沒法用它,還是老老實實做老古董。 關鍵字yield沒錯,yield是個關鍵字,不是函數。關鍵字用來干啥?它的作用是“命令”。和var不同,不是用來聲明,但是和return一樣,用來告知程序某種狀態(tài),return告訴程序要返回什么值(也意味著結束,結束的時候才會返回值嘛),而yield告訴程序當前的狀態(tài)值,而且你運行到這里給我暫停一下。 因為yield是命令型的關鍵字,所以它的用法是:
rv是可選的,這里不是說它返回一個數組。yield后面的表達式也是可選的。yield的返回值是一個狀態(tài)值。如果從返回值的角度講,yield還可以當做是一種運算符,但是由于它的作用是暫停和恢復,所以嚴格意義上說,不能叫運算符,運算符是用來運算的,而yield是用來“命令”的。 遍歷器函數(Generator)這是ES6里面新增的Generator,為了把它和我們已知的東西聯系起來,我把它翻譯為遍歷器函數,但是實際上現在還沒有統一的叫法,坐等大神們開宗立派。 我個人的理解,Generator函數的最大用處就是用來生成一個遍歷器。 不過它是一個函數,所以和普通的函數有點區(qū)別,因此在聲明函數的時候,要在function和函數名之間加一個*號:
而yield也必須在Generator函數中才有意義,脫離了Generator就沒意義了啊,這就像說“鯊魚是用來在海里面消滅傻瓜魚的”現在你吧鯊魚丟到陸地上,請問它有啥意義?給人做包包嗎? Generator函數的一個重要特點就是需要執(zhí)行next()方法才能運行,聲明好它之后,根本不會馬上運行。舉個栗子:
上面的例子如此簡單,你不會看不懂吧?現在告訴你,alert的結果是0! 我前面都說了,要執(zhí)行netx()方法才會運行。所以在上面的代碼末尾添加:
沒怎明白?繼續(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()那段代碼改成下面的:
第一個console的地方,因為第一次執(zhí)行f.next(),遇到yield就暫停了,得到s1這個狀態(tài),這個狀態(tài)的value是yield后面緊跟的表達式的值,done表示遍歷沒有結束,還可以繼續(xù)執(zhí)行next()方法。第二次再執(zhí)行f.next()的時候,遇到了return,但后面沒有表達式,所以返回值是undefined,一旦遇到return就表示遍歷可以結束了,所以done為true。 當運行到 也正是因為有.next()方法,所以它叫遍歷器。for...of,...,Array.from都是利用遍歷器接口進行運算的,所以如果沒有Generator,這幾個方法就用不了。而在Babel里面,babel-core本身默認不支持Generator,所以這幾個運算其實也需要另外的模塊( babel-polyfill )來支持。 一不小心講到了babel,罪過罪過。 .next()的參數造孽的地方終于要來了。一言不合,就上代碼:
求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??! 第二個問題,其實可以不用加括號,但是如果不加括號的話,那么后面的+運算就沒了,比如說上面的代碼改為:
這差距就大了,yield x是一坨,不會等到你x+y它就會返回,所以b還是1. 把yield當變量看上面這一個小節(jié)你有沒有發(fā)現一個神奇的規(guī)律?就是你在給next()傳參的時候,總是對應某一個yield,把這個yield以及后面緊跟著的表達式用傳入的參數代替。于是乎,下面這個代碼你就懂了:
上面這個代碼里面,竟然把yield后面的表達式給去掉了。那會有啥影響?b.value將等于undefined,僅此而已。因為b這個位置就是得到yield [空]的結果,所以就是undefined咯。但是當foo.next(4)執(zhí)行的時候,不管你上面是不是undefined,我現在就是要用4來覆蓋你,所以z.value還是4. 也正因為這樣,在字符串里面,可以這樣使用:
上面這個例子把yield當做一個變量來使用,如果你感興趣,可以想辦法吧第一次的時候錯誤去除掉。 這個用法看上去很挫,但是很厲害的是,可以通過這個思路,實現字符串模板的次第渲染??梢钥刂其秩镜侥膫€位置。 總結一下寫了這么多,總結一下yield,實際上:
最后,說一下for...of是怎么運行的。
for...of在遍歷foo()返回的結果時,每遇到一個yield,就把yield后面表達式的結果作為of前面的v的值進行賦值(next()返回值的value字段)。沒錯,就這么不要臉的解釋完了。 最后的最后,這篇文章不是為了解釋和學習Generator的,所以要了解更多Generator,請繼續(xù)關注我的博客:) |
|