【第808期】你不懂JS:ES6與將來 語法(中)

圖片

前言前端

又是一篇會讓你們Mark的文章,今天繼續連載《你不懂JS》系列,本文由前端早讀課專欄做者@HetfieldJoe受權分享。正則表達式


正文從這開始~編程


語法(上)詳見:【第798期】你不懂JS:ES6與將來 語法(上)數組


對象字面量擴展安全

ES6給不起眼兒的{ .. }對象字面量增長了幾個重要的便利擴展。ide


簡約屬性函數

你必定很熟悉用這種形式的對象字面量聲明:工具

圖片


若是處處說x: x老是讓你感到繁冗,那麼有個好消息。若是你須要定義一個名稱和詞法標識符一致的屬性,你能夠將它從x: x縮寫爲x。考慮以下代碼:oop

圖片


簡約方法this

本着與咱們剛剛檢視的簡約屬性相同的精神,添附在對象字面量屬性上的函數也有一種便利簡約形式。


之前的方式:

圖片


而在ES6中:

圖片


警告: 雖然x() { .. }看起來只是x: function(){ .. }的縮寫,可是簡約方法有一種特殊行爲,是它們對應的老方式所不具備的;確切地說,是容許super(參見本章稍後的「對象super」)的使用。


Generator(見第四章)也有一種簡約方法形式:

圖片


簡約匿名

雖然這種便利縮寫十分誘人,可是這其中有一個微妙的坑要當心。爲了展現這一點,讓咱們檢視一下以下的前ES6代碼,你可能會試着使用簡約方法來重構它:

圖片


這段蠢代碼只是生成兩個隨機數,而後用大的減去小的。但這裏重要的不是它作的是什麼,而是它是如何被定義的。讓我把焦點放在對象字面量和函數定義上,就像咱們在這裏看到的:

圖片

爲何咱們同時說something:和function something?這不是冗餘嗎?實際上,不是,它們倆被用於不一樣的目的。屬性something讓咱們可以調用o.something(..),有點兒像它的公有名稱。可是第二個something是一個詞法名稱,使這個函數能夠爲了遞歸而從內部引用它本身。


你能看出來爲何return something(y,x)這一行須要名稱something來引用這個函數嗎?由於這裏沒有對象的詞法名稱,要是有的話咱們就能夠說return o.something(y,x)或者其餘相似的東西。


當一個對象字面量的確擁有一個標識符名稱時,這實際上是一個很常見的作法,好比:

圖片

這是個好主意嗎?也許是,也許不是。你在假設名稱controller將老是指向目標對象。但它也極可能不是 —— 函數makeRequest(..)不能控制外部的代碼,所以不能強制你的假設必定成立。這可能會回過頭來咬到你。


另外一些人喜歡使用this定義這樣的東西:

圖片

這看起來不錯,並且若是你老是用controller.makeRequest(..)來調用方法的話它就應該能工做。但如今你有一個this綁定的坑,若是你作這樣的事情的話:

圖片

固然,你能夠經過傳遞controller.makeRequest.bind(controller)做爲綁定到事件上的處理器引用來解決這個問題。可是這很討厭 —— 它不是很吸引人。


或者要是你的內部this.makeRequest(..)調用須要從一個嵌套的函數內發起呢?你會有另外一個this綁定災難,人們常用var self = this這種用黑科技解決,就像:

圖片

更討厭。


注意: 更多關於this綁定規則和陷阱的信息,參見本系列的 this與對象原型 的第一到二章。


好了,這些與簡約方法有什麼關係?回想一下咱們的something(..)方法定義:

圖片

在這裏的第二個something提供了一個超級便利的詞法標識符,它老是指向函數本身,給了咱們一個可用於遞歸,事件綁定/解除等等的完美引用 —— 不用亂搞this或者使用不可靠的對象引用。


太好了!


那麼,如今咱們試着將函數引用重構爲這種ES6解約方法的形式:

圖片

第一眼看上去不錯,除了這個代碼將會壞掉。return something(..)調用經不會找到something標識符,因此你會獲得一個ReferenceError。噢,但爲何?


上面的ES6代碼段將會被翻譯爲:

圖片


仔細看。你看出問題了嗎?簡約方法定義暗指something: function(x,y)。看到咱們依靠的第二個something是如何被省略的了嗎?換句話說,簡約方法暗指匿名函數表達式。


對,討厭。


注意: 你可能認爲在這裏=>箭頭函數是一個好的解決方案。可是它們也一樣不夠,由於它們也是匿名函數表達式。咱們將在本章稍後的「箭頭函數」中講解它們。


一個部分地補償了這一點的消息是,咱們的簡約函數something(x,y)將不會是徹底匿名的。參見第七章的「函數名」來了解ES6函數名稱的推斷規則。這不會在遞歸中幫到咱們,可是它至少在調試時有用處。


那麼咱們怎樣總結簡約方法?它們簡短又甜蜜,並且很方便。可是你應當僅在你永遠不須要將它們用於遞歸或事件綁定/解除時使用它們。不然,就堅持使用你的老式something: function something(..)方法定義。


你的不少方法都將可能從簡約方法定義中受益,這是個很是好的消息!只要當心幾處未命名的災難就好。


ES5 Getter/Setter

技術上講,ES5定義了getter/setter字面形式,可是看起來它們沒有被太多地使用,這主要是因爲缺少轉譯器來處理這種新的語法(其實,它是ES5中加入的惟一的主要新語法)。因此雖然它不是一個ES6的新特性,咱們也將簡單地複習一下這種形式,由於它可能會隨着ES6的向前發展而變得有用得多。


考慮以下代碼:

圖片


這些getter和setter字面形式也能夠出如今類中;參見第三章。


警告: 可能不太明顯,可是setter字面量必須剛好有一個被聲明的參數;省略它或羅列其餘的參數都是不合法的語法。這個單獨的必須參數 能夠 使用解構和默認值(例如,set id({ id: v = 0 }) { .. }),可是收集/剩餘...是不容許的(set id(...v) { .. })。


計算型屬性名

你可能曾經遇到過像下面的代碼段那樣的狀況,你的一個或多個屬性名來自於某種表達式,所以你不能將它們放在對象字面量中:

圖片


ES6爲對象字面定義增長了一種語法,它容許你指定一個應當被計算的表達式,其結果就是被賦值屬性名。考慮以下代碼:

圖片


任何合法的表達式均可以出如今位於對象字面定義的屬性名位置的[ .. ]內部。


頗有可能,計算型屬性名最常常與Symbol(咱們將在本章稍後的「Symbol」中講解)一塊兒使用,好比:

圖片


Symbol.toStringTag是一個特殊的內建值,咱們使用[ .. ]語法求值獲得,因此咱們能夠將值"really cool thing"賦值給這個特殊的屬性名。


計算型屬性名還能夠做爲簡約方法或簡約generator的名稱出現:

圖片

設置[[Prototype]]

咱們不會在這裏講解原型的細節,因此關於它的更多信息,參見本系列的 this與對象原型。


有時候在你聲明對象字面量的同時給它的[[Prototype]]賦值頗有用。下面的代碼在一段時期內曾經是許多JS引擎的一種非標準擴展,可是在ES6中獲得了標準化:

圖片

o2是用一個對象字面量聲明的,但它也被[[Prototype]]連接到了o1。這裏的__proto__屬性名還能夠是一個字符串"__proto__",可是要注意它 不能 是一個計算型屬性名的結果(參見前一節)。


客氣點兒說,__proto__是有爭議的。在ES6中,它看起來是一個最終被很勉強地標準化了的,幾十年前的自主擴展功能。實際上,它屬於ES6的「Annex B」,這一部分羅列了JS感受它僅僅爲了兼容性的緣由,而不得不標準化的東西。


警告: 雖然我勉強贊同在一個對象字面定義中將__proto__做爲一個鍵,但我絕對不贊同在對象屬性形式中使用它,就像o.__proto__。這種形式既是一個getter也是一個setter(一樣也是爲了兼容性的緣由),但絕對存在更好的選擇。更多信息參見本系列的 this與對象原型。


對於給一個既存的對象設置[[Prototype]],你可使用ES6的工具Object.setPrototypeOf(..)。考慮以下代碼:

圖片


注意: 咱們將在第六章中再次討論Object。「Object.setPrototypeOf(..)靜態函數」提供了關於Object.setPrototypeOf(..)的額外細節。另外參見「Object.assign(..)靜態函數」來了解另外一種將o2原型關聯到o1的形式。


對象super

super一般被認爲是僅與類有關。然而,因爲JS對象僅有原型而沒有類的性質,super是一樣有效的,並且在普通對象的簡約方法中行爲幾乎同樣。


考慮以下代碼:

圖片


警告: super僅在簡約方法中容許使用,而不容許在普通的函數表達式屬性中。並且它還僅容許使用super.XXX形式(屬性/方法訪問),而不是super()形式。


在方法o2.foo()中的super引用被靜態地鎖定在了o2,並且明確地說是o2的[[Prototype]]。這裏的super基本上是Object.getPrototypeOf(o2) —— 顯然被解析爲o1 —— 這就是他如何找到並調用o1.foo()的。


關於super的完整細節,參見第三章的「類」。


模板字面量

在這一節的最開始,我將不得不呼喚這個ES6特性的極其……誤導人的名稱,這要看在你的經驗中 模板(template) 一詞的含義是什麼。


許多開發者認爲模板是一段可複用的,可重繪的文本,就像大多數模板引擎(Mustache,Handlebars,等等)提供的能力那樣。ES6中使用的 模板 一詞暗示着類似的東西,就像一種聲明能夠被重繪的內聯模板字面量的方法。然而,這根本不是考慮這個特性的正確方式。


因此,在咱們繼續以前,我把它重命名爲它本應被稱呼的名字:插值型字符串字面量(或者略稱爲 插值型字面量)。


你已經十分清楚地知道了如何使用"或'分隔符來聲明字符串字面量,並且你還知道它們不是(像有些語言中擁有的)內容將被解析爲插值表達式的 智能字符串。


可是,ES6引入了一種新型的字符串字面量,使用反引號`做爲分隔符。這些字符串字面量容許嵌入基本的字符串插值表達式,以後這些表達式自動地被解析和求值。


這是老式的前ES6方式:

圖片


如今,考慮這種新的ES6方式:

圖片


如你所見,咱們在一系列被翻譯爲字符串字面量的字符周圍使用了,可是${..}形式中的任何表達式都將當即內聯地被解析和求值。稱呼這樣的解析和求值的高大上名詞就是 插值(interpolation)(比模板要準確多了)。


被插值的字符串字面量表達式的結果只是一個老式的普通字符串,賦值給變量greeting。


警告: typeof greeting == "string"展現了爲何不將這些實體考慮爲特殊的模板值很重要,由於你不能將這種字面量的未求值形式賦值給某些東西並複用它。`..`字符串字面量在某種意義上更像是IIFE,由於它自動內聯地被求值。`..`字符串字面量的結果只不過是一個簡單的字符串。


插值型字符串字面量的一個真正的好處是他們容許被分割爲多行:

image.png

在插值型字符串字面量中的換行將會被保留在字符串值中。


除非在字面量值中做爲明確的轉義序列出現,回車字符\r(編碼點U+000D)的值或者回車+換行序列\r\n(編碼點U+000D和U+000A)的值都會被泛化爲一個換行字符\n(編碼點U+000A)。但不要擔憂;這種泛化不多見並且極可能僅會在你將文本拷貝粘貼到JS文件中時纔會發生。


插值表達式

在一個插值型字符串字面量中,任何合法的表達式都被容許出如今${..}內部,包括函數調用,內聯函數表達式調用,甚至是另外一個插值型字符串字面量!


考慮以下代碼:

image.png


當咱們組合變量who與字符串s時, 相對於who + "s",這裏的內部插值型字符串字面量`${who}s`更方便一些。有些狀況下嵌套的插值型字符串字面量是有用的,可是若是你發現本身作這樣的事情太頻繁,或者發現你本身嵌套了好幾層時,你就要當心一些。


若是確實有這樣狀況,你的字符串你值生產過程極可能能夠從某些抽象中獲益。


警告: 做爲一個忠告,使用這樣的新發現的力量時要很是當心你代碼的可讀性。就像默認值表達式和解構賦值表達式同樣,僅僅由於你 能 作某些事情,並不意味着你 應該 作這些事情。在使用新的ES6技巧時千萬不要作過了頭,使你的代碼比你或者你的其餘隊友聰明。


表達式做用域

關於做用域的一個快速提醒是它用於解析表達式中的變量時。我早先提到過一個插值型字符串字面量與IIFE有些相像,事實上這也能夠考慮爲做用域行爲的一種解釋。


考慮以下代碼:

圖片


在函數bar()內部,字符串字面量`..`被表達的那一刻,可供它查找的做用域發現變量的name的值爲"bar"。既不是全局的name也不是foo(..)的name。換句話說,一個插值型字符串字面量在它出現的地方是詞法做用域的,而不是任何方式的動態做用域。


標籤型模板字面量

再次爲了合理性而重命名這個特性:標籤型字符串字面量。


老實說,這是一個ES6提供的更酷的特性。它可能看起來有點兒奇怪,並且也許一開始看起來通常不那麼實用。但一旦你花些時間在它上面,標籤型字符串字面量的用處可能會令你驚訝。


例如:

圖片


讓咱們花點兒時間考慮一下前面的代碼段中發生了什麼。首先,跳出來的最刺眼的東西就是foo`Everything...`;。它看起來不像是任何咱們曾經見過的東西。不是嗎?


它實質上是一種不須要( .. )的特殊函數調用。標籤 —— 在字符串字面量`..`以前的foo部分 —— 是一個應當被調用的函數的值。實際上,它能夠是返回函數的任何表達式,甚至是一個返回另外一個函數的函數調用,就像:

image.png


可是看成爲一個字符串字面量的標籤時,函數foo(..)被傳入了什麼?


第一個參數值 —— 咱們稱它爲strings —— 是一個全部普通字符串的數組(全部被插值的表達式之間的東西)。咱們在strings數組中獲得兩個值:"Everything is "和"!"。


以後爲了咱們示例的方便,咱們使用...收集/剩餘操做符(見本章早先的「擴散/剩餘」部分)將全部後續的參數值收集到一個稱爲values的數組中,雖然說你原本固然能夠把它們留做參數strings後面單獨的命名參數。


被收集進咱們的values數組中的參數值,就是在字符串字面量中發現的,已經被求過值的插值表達式的結果。因此在咱們的例子中values裏惟一的元素顯然就是awesome。


你能夠將這兩個數組考慮爲:在values中的值本來是你拼接在stings的值之間的分隔符,並且若是你將全部的東西鏈接在一塊兒,你就會獲得完整的插值字符串值。


一個標籤型字符串字面量像是一個在插值表達式被求值以後,可是在最終的字符串被編譯以前的處理步驟,容許你在從字面量中產生字符串的過程當中進行更多的控制。


通常來講,一個字符串字面連標籤函數(在前面的代碼段中是foo(..))應當計算一個恰當的字符串值並返回它,因此你可使用標籤型字符串字面量做爲一個未打標籤的字符串字面量來使用:

image.png


在這個代碼段中,tag(..)是一個直通操做,由於它不實施任何特殊的修改,而只是使用reduce(..)來循環遍歷,並像一個未打標籤的字符串字面量同樣,將strings和values拼接/穿插在一塊兒。


那麼實際的用法是什麼?有許多高級的用法超出了咱們要在這裏討論的範圍。但這裏有一個格式化美圓數字的簡單想法(有些像基本的本地化):

image.png

若是在values數組中遇到一個number值,咱們就在它前面放一個"$"並用toFixed(2)將它格式化爲小數點後兩位有效。不然,咱們就不碰這個值而讓它直經過去。


原始字符串

在前一個代碼段中,咱們的標籤函數接受的第一個參數值稱爲strings,是一個數組。可是有一點兒額外的數據被包含了進來:全部字符串的原始未處理版本。你可使用.raw屬性訪問這些原始字符串值,就像這樣:

圖片

原始版本的值保留了原始的轉義序列\n(`和n`是兩個分離的字符),但處理過的版本認爲它是一個單獨的換行符。可是,早先提到的行終結符泛化操做,是對兩個值都實施的。


ES6帶來了一個內建函數,它能夠用作字符串字面量的標籤:String.raw(..)。它簡單地直通strings值的原始版本:

image.png

字符串字面量標籤的其餘用法包括國際化,本地化,和許多其餘的特殊處理。


箭頭函數

咱們在本章早先接觸了函數中this綁定的複雜性,並且在本系列的 this與對象原型 中也以至關的篇幅講解過。理解普通函數中基於this的編程帶來的挫折是很重要的,由於這是ES6的新=>箭頭函數的主要動機。


做爲與普通函數的比較,咱們首先來展現一下箭頭函數看起來什麼樣:

圖片


箭頭函數的定義由一個參數列表(零個或多個參數,若是參數不是隻有一個,須要有一個( .. )包圍這些參數)組成,緊跟着是一個=>符號,而後是一個函數體。


因此,在前面的代碼段中,箭頭函數只是(x,y) => x + y這一部分,而這個函數的引用恰好被賦值給了變量foo。


函數體僅在含有多於一個表達式,或者由一個非表達式語句組成時才須要用{ .. }括起來。若是僅含有一個表達式,並且你省略了外圍的{ .. },那麼在這個表達式前面就會有一個隱含的return,就像前面的代碼段中展現的那樣。


這裏是一些其餘種類的箭頭函數:

圖片

箭頭函數 老是 函數表達式;不存在箭頭函數聲明。並且很明顯它們都是匿名函數表達式 —— 它們沒有能夠用於遞歸或者事件綁定/解除的命名引用 —— 但在第七章的「函數名」中將會講解爲了調試的目的而存在的ES6函數名接口規則。


注意: 普通函數參數的全部功能對於箭頭函數都是可用的,包括默認值,解構,剩餘參數,等等。


箭頭函數擁有漂亮,簡短的語法,這使得它們在表面上看起來對於編寫簡潔代碼頗有吸引力。確實,幾乎全部關於ES6的文獻(除了這個系列中的書目)看起來都當即將箭頭函數僅僅認做「新函數」。

這說明在關於箭頭函數的討論中,幾乎全部的例子都是簡短的單語句工具,好比那些做爲回調傳遞給各類工具的箭頭函數。例如:

圖片

在這些狀況下,你的內聯函數表達式很適合這種在一個單獨語句中快速計算並返回結果的模式,對於更繁冗的function關鍵字和語法來講箭頭函數確實看起來是一個很吸人,並且輕量的替代品。


大多數人看着這樣簡潔的例子都傾向於發出「哦……!啊……!」的感嘆,就像我想象中你剛剛作的那樣!


然而我要警示你的是,在我看來,使用箭頭函數的語法代替普通的,多語句函數,特別是那些能夠被天然地表達爲函數聲明的函數,是某種誤用。


回憶本章早前的字符串字面量標籤函數dollabillsyall(..) —— 讓咱們將它改成使用=>語法:圖片


在這個例子中,我作的惟一修改是刪除了function,return,和一些{ .. },而後插入了=>和一個var。這是對代碼可讀性的重大改進嗎?呵呵。


實際上我會爭論,缺乏return和外部的{ .. }在某種程度上模糊了這樣的事實:reduce(..)調用是函數dollabillsyall(..)中惟一的語句,並且它的結果是這個調用的預期結果。另外,那些受過訓練而習慣於在代碼中搜索function關鍵字來尋找做用域邊界的眼睛,如今須要搜索=>標誌,在密集的代碼中這絕對會更加困難。


雖然不是一個硬性規則,可是我要說從=>箭頭函數轉換得來的可讀性,與被轉換的函數長度成反比。函數越長,=>能幫的忙越少;函數越短,=>的閃光之處就越多。


我以爲這樣作更明智也更合理:在你須要短的內聯函數表達式的地方採用=>,但保持你的通常長度的主函數原封不動。


不僅是簡短的語法,而是this

曾經集中在=>上的大多數注意力都是它經過在你的代碼中除去function,return,和{ .. }來節省那些寶貴的擊鍵。


可是至此咱們一直忽略了一個重要的細節。我在這一節最開始的時候說過,=>函數與this綁定行爲密切相關。事實上,=>箭頭函數 主要的設計目的 就是以一種特定的方式改變this的行爲,解決在this敏感的編碼中的一個痛點。


節省擊鍵是掩人耳目的東西,至可能是一個誤導人的配角。


讓咱們重溫本章早前的另外一個例子:

圖片


咱們使用了黑科技var self = this,而後引用了self.makeRequest(..),由於在咱們傳遞給addEventListener(..)的回調函數內部,this綁定將與makeRequest(..)自己中的this綁定不一樣。換句話說,由於this綁定是動態的,咱們經過self變量退回到了可預測的詞法做用域。


在這其中咱們終於能夠看到=>箭頭函數主要的設計特性了。在箭頭函數內部,this綁定不是動態的,而是詞法的。在前一個代碼段中,若是咱們在回調裏使用一個箭頭函數,this將會不出所料地成爲咱們但願它成爲的東西。


考慮以下代碼:

圖片

前面代碼段的箭頭函數中的詞法this如今指向的值與外圍的makeRequest(..)函數相同。換句話說,=>是var self = this的語法上的替代品。

在var self = this(或者,另外一種選擇是,.bind(this)調用)一般能夠幫忙的狀況下,=>箭頭函數是一個基於相同原則的很好的替代操做。聽起來很棒,是吧?


沒那麼簡單。


若是=>取代var self = this或.bind(this)能夠工做,那麼猜猜=>用於一個 不須要 var self = this就能工做的this敏感的函數會發生麼?你可能會猜到它將會把事情搞砸。沒錯。

考慮以下代碼:

圖片

雖然咱們以controller.makeRequest(..)的方式進行了調用,可是this.helper引用失敗了,由於這裏的this沒有像日常那樣指向controller。那麼它指向哪裏?它經過詞法繼承了外圍的做用域中的this。在前面的代碼段中,它是全局做用域,this指向了全局做用域。呃。


除了詞法的this之外,箭頭函數還擁有詞法的arguments —— 它們沒有本身的arguments數組,而是從它們的上層繼承下來 —— 一樣還有詞法的super和new.target(參見第三章的「類」)。


因此,關於=>在什麼狀況下合適或不合適,咱們如今能夠推論出一組更加微妙的規則:


若是你有一個簡短的,單語句內聯函數表達式,它惟一的語句是某個計算後的值的return語句,而且 這個函數沒有在它內部製造一個this引用,而且 沒有自引用(遞歸,事件綁定/解除),而且 你合理地預期這個函數毫不會變得須要this引用或自引用,那麼你就可能安全地將它重構爲一個=>箭頭函數。


若是你有一個內部函數表達式,它依賴於外圍函數的var self = this黑科技或者.bind(this)調用來確保正確的this綁定,那麼這個內部函數表達式就可能安全地變爲一個=>箭頭函數。


若是你有一個內部函數表達式,它依賴於外圍函數的相似於var args = Array.prototype.slice.call(arguments)這樣的東西來製造一個arguments的詞法拷貝,那麼這個內部函數就可能安全地變爲一個=>箭頭函數。


對於其餘的全部東西 —— 普通函數聲明,較長的多語句函數表達式,須要詞法名稱標識符進行自引用(遞歸等)的函數,和任何其餘不符合前述性質的函數 —— 你就可能應當避免=>函數語法。


底線:=>與this,arguments,和super的詞法綁定有關。它們是ES6爲了修正一些常見的問題而被有意設計的特性,而不是爲了修正bug,怪異的代碼,或者錯誤。


不要相信任何說=>主要是,或者幾乎是,爲了減小几下擊鍵的炒做。不管你是省下仍是浪費了這幾下擊鍵,你都應當確切地知道你打入的每一個字母是爲了作什麼。


提示: 若是你有一個函數,因爲上述各類清楚的緣由而不適合成爲一個=>箭頭函數,但同時它又被聲明爲一個對象字面量的一部分,那麼回想一下本章早先的「簡約方法」,它有簡短函數語法的另外一種選擇。


對於如何/爲什麼選用一個箭頭函數,若是你喜歡一個可視化的決策圖的話:

圖片


for..of Loops

伴隨着咱們熟知的JavaScriptfor和for..in循環,ES6增長了一個for..of循環,它循環遍歷一組由一個 迭代器(iterator) 產生的值。


你使用for..of循環遍歷的值必須是一個 可迭代對象(iterable),或者它必須是一個能夠被強制轉換/封箱(參見本系列的 類型與文法)爲一個可迭代對象的值。一個可迭代對象只不過是一個能夠生成迭代器的對象,而後由循環使用這個迭代器。


讓咱們比較for..of與for..in來展現它們的區別:

圖片


如你所見,for..in循環遍歷數組a中的鍵/索引,而for.of循環遍歷a中的值。


這是前面代碼段中for..of的前ES6版本:

圖片


而這是一個ES6版本的非for..of等價物,它同時展現了手動迭代一個迭代器(見第三章的「迭代器」):

圖片


在幕後,for..of循環向可迭代對象要來一個迭代器(使用內建的Symbol.iterator;參見第七章的「通用Symbols」),而後反覆調用這個迭代器並將它產生的值賦值給循環迭代的變量。


在JavaScript標準的內建值中,默認爲可迭代對象的(或提供可迭代能力的)有:

  • 數組

  • 字符串

  • Generators(見第三章)

  • 集合/類型化數組(見第五章)


警告: 普通對象默認是不適用於for..of循環的。由於他們沒有默認的迭代器,這是有意爲之的,不是一個錯誤。可是,咱們不會進一步探究這其中微妙的緣由。在第三章的「迭代器」中,咱們將看到如何爲咱們本身的對象定義迭代器,這容許for..of遍歷任何對象來獲得咱們定義的一組值。


這是如何遍歷一個基本類型的字符串中的字符:

圖片


基本類型字符串"hello"被強制轉換/封箱爲等價的String對象包裝器,它是默認就是一個可迭代對象。


在for (XYZ of ABC)..中,XYZ子句既能夠是一個賦值表達式也能夠是一個聲明,這與for和for..in中相同的子句如出一轍。因此你能夠作這樣的事情:

image.png


與其餘的循環同樣,使用break,continue,return(若是是在一個函數中),以及拋出異常,for..of循環能夠被提早終止。在任何這些狀況下,迭代器的return(..)函數(若是存在的話)都會被自動調用,以便讓迭代器進行必要的清理工做。


注意: 可迭代對象與迭代器的完整內容參見第三章的「迭代器」。


正則表達式

讓咱們認可吧:長久以來在JS中正則表達式都沒怎麼改變過。因此一件很棒的事情是,在ES6中它們終於學會了一些新招數。咱們將在這裏簡要地講解一下新增的功能,可是正則表達式總體的話題是如此厚重,以致於若是你須要複習一下的話你須要找一些關於它的專門章節/書籍(有許多!)。


Unicode標誌

咱們將在本章稍後的「Unicode」一節中講解關於Unicode的更多細節。在此,咱們將僅僅簡要地看一下ES6+正則表達式的新u標誌,它使這個正則表達式的Unicode匹配成爲可能。


JavaScript字符串一般被解釋爲16位字符的序列,它們對應於 基本多文種平面(Basic Multilingual Plane (BMP)) (http://en.wikipedia.org/wiki/Plane_%28Unicode%29)中的字符。可是有許多UTF-16字符在這個範圍之外,並且字符串可能含有這些多字節字符。


在ES6以前,正則表達式只能基於BMP字符進行匹配,這意味着在匹配時那些擴展字符被看做是兩個分離的字符。這一般不理想。


因此,在ES6中,u標誌告訴正則表達式使用Unicode(UTF-16)字符的解釋方式來處理字符串,這樣一來一個擴展的字符將做爲一個單獨的實體被匹配。


警告: 儘管名字的暗示是這樣,可是「UTF-16」並不嚴格地意味着16位。現代的Unicode使用21位,並且像UTF-8和UTF-16這樣的標準大致上是指有多少位用於表示一個字符。


一個例子(直接從ES6語言規範中拿來的):

相關文章
相關標籤/搜索