[書籍精讀]《你不知道的JavaScript(下卷)》精讀筆記分享

寫在前面

  • 書籍介紹:JavaScript這門語言簡單易用,很容易上手,但其語言機制複雜微妙,即便是經驗豐富的JavaScript開發人員,若是沒有認真學習的話也沒法真正理解。本套書直面當前JavaScript開發人員不求甚解的大趨勢,深刻理解語言內部的機制,全面介紹了JavaScript中常被人誤解和忽視的重要知識點。
  • 個人簡評:《你不知道的JavaScript》系列分上中下三卷,這裏是下卷,主要講解ES6相關的。該系列書籍本人以爲就上卷寫的不錯,中卷有些冗餘,下卷講ES6比較粗糙。不過有空翻一翻,仍是多少有些收穫。
  • !!文末有pdf書籍、筆記思惟導圖、隨書代碼打包下載地址,須要請自取!閱讀「書籍精讀系列」全部筆記,請移步:推薦收藏-JavaScript書籍精讀筆記系列導航

第1章 ES?如今與將來

  • 與ES5不一樣,ES6並不只僅是爲這個語言新增一組API。它包括一組新的語法形式,其中的一部分多是要花些時間才能理解和熟悉的。它還包括各類各樣的新的組織形式和操做各類數據類型的新的輔助API

1.2.transpiling

  • transpiling(transformation+compiling,轉換+編譯)的技術。簡單的說,其思路是利用專門的工具把你的ES6代碼轉換爲等價(或近似)的能夠在ES5環境下工做的代碼
  • 並不是全部的ES6新特性都須要使用transpiler,還有polyfill(也稱爲shim)這種模式。在可能的狀況下,polyfill會爲新環境中的行爲定義在舊環境中的等價行爲。語法不能polyfill,而API一般能夠

第2章 語法

2.1.塊做用域聲明

  • JavaScript中變量做用域的基本單元一直是function。若是須要建立一個塊做用域,最廣泛的方法除了普通的函數聲明以外,就是當即調用函數表達式(IIFE)
  • let 聲明:能夠建立綁定到任意塊的聲明。只須要一對{..}就能夠建立一個做用域;過早訪問let 聲明的引用致使的這個ReferenceError嚴格說叫作臨時死亡區錯誤(TDZ)--你在訪問一個已經聲明但沒有初始化的變量;聲明時沒有賦值的變量會自動賦值爲undefined,因此let b;就等價於let b = undefined;;未聲明的變量,因此typeof是檢查它是否存在的惟一安全的方法;爲何我堅持認爲應該把全部的let聲明放在其所在做用域的最前面了,這樣就徹底避免了不當心過早訪問的問題;
  • const聲明:const ,用於建立常量;它是一個設定了初始值以後就只讀的變量;const 聲明必需要有顯式的初始化;若是這個值是複雜值,好比對象或者數組,其內容仍然是能夠修改的;是否使用const,理論上說,引擎更容易瞭解這個變量的值/類型永遠不會改變,那麼它就能夠取消某些可能的追蹤;
  • 塊做用域函數:從ES6開始,塊內聲明的函數,其做用域在這個塊內

2.2.spread/rest

  • 當...用在數組以前時(其實是任何iterable),它會把這個變量「展開」爲各個獨立的值
  • ...的另一種常見用法基本上能夠被看做反向的行爲;...把一系列值收集到一塊兒成爲一個數組

2.3.默認參數值

  • 可能JavaScript最多見的一個技巧就是關於設定函數參數默認值的
  • 只能經過傳入比「指望」更少的參數來省略最後的若干參數(例如,後側的),而沒法省略位於參數列表中間或者起始處的參數
  • 一個很重要的須要記住的JavaScript設計原則:undefined意味着缺失。也就是說,undefined和缺失是沒法區別的,至少對於函數參數來講是如此
  • 默認值表達式:默認值表達式是惰性求值的,這意味着它們只在須要的時候運行--也就是說,是在參數的值省略或者爲undefined的時候;Function.prototype自己就是一個沒有操做的空函數;

2.4.解構

  • 能夠把將數組或者對象屬性中帶索引的值手動賦值看做結構化賦值。專門語法,專用於數組解構和對象解構
  • 不僅是聲明:除非須要把全部的賦值表達式都看成聲明,不然不該該在賦值中混入聲明。否則會出現語法錯誤
  • 重複賦值:對象解構形式容許屢次列出同一個源屬性(持有值類型任意);記住:解構的目的不僅是爲了打字更少,而是爲了可讀性更強;

2.5.太多,太少,剛恰好

  • 若是爲比解構/分解出來的值更多的值賦值,那麼就像指望的同樣,多餘的值會被賦爲undefined
  • 默認值賦值:使用與前面默認函數參數值相似的=語法,解構的兩種形式均可以提供一個用來賦值的默認值
  • 嵌套解構:能夠把嵌套解構看成一種展開對象名字空間的簡單方法
  • 解構參數:解構賦值/默認值會被放在參數列表中,而重組的過程會被放在函數體的return語句中

2.6.對象字面量擴展

  • 簡潔屬性
  • 簡潔方法:嚴格說來,ES5定義了getter/setter字面量形式,可是沒怎麼被使用,主要是由於缺乏transpiler來處理這個新語法(實際上也是ES5新增的惟一主要新語法)
  • 計算屬性名:對象字面定義屬性名位置的[..]中能夠放置任意合法表達式;計算屬性名最多見的用法可能就是和Symbols共同使用;
  • 設定[[Prototype]]
  • super對象

2.7.模板字面量

  • 這個特性的名稱很是具備誤導性,其根據你對單詞模板(template)的理解而定
  • 插入字符串字面量中的換行(新行)會在字符串值中被保留
  • 插入表達式:在插入字符串字面量的${..}內能夠出現任何合法的表達式,包括函數調用、在線函數表達式調用,甚至其餘插入字符串字面量;插入字符串字面量在它出現的詞法做用域內,沒有任何形式的動態做用域;
  • 標籤模板字面量:從新命名這個特性來明確表達其功能:標籤字符串字面量;哪些實際應用(用來把數字格式化爲美圓表示法;其餘應用包括全球化、本地化等的特殊處理)

2.8.箭頭函數

  • 箭頭函數定義包括一個參數列表(零個或多個參數,若是參數個數不是一個的話要用(..)包圍起來),而後是標識=>,函數體放在最後
  • 箭頭函數老是函數表達式;並不存在箭頭函數聲明
  • 箭頭函數是匿名函數表達式--他們沒有用於遞歸或者事件綁定/解綁定的命名引用
  • 我認爲=>箭頭函數轉變帶來的可讀性與被轉化函數的長度負相關。這個函數越長,=>帶來的好處就越小;函數越短,=>帶來的好處就越大
  • 不僅是更短的語法,而是this:對=>的關注多數都在於從代碼中去掉function、return和{..}節省了那些寶貴的鍵盤輸入;對=>的關注多數都在於從代碼中去掉function、return和{..}節省了那些寶貴的鍵盤輸入;在箭頭函數內部,this綁定不是動態的,而是詞法的;底線:=>是關於this、arguments和super的詞法綁定。這個ES6的特性設計用來修正一些常見的問題,而不是bug、巧合或者錯誤

2.9.for...of循環

  • for..of循環的值必須是一個iterable,或者說它必須能夠轉換/封箱到一個iterable對象的值。iterable就是一個可以產生迭代器供循環使用的對象
  • JavaScript中默認爲(或提供)iterable的標準內建值包括:Arrays、Strings、Generators、Collections/TypedArrays
  • for..of循環也能夠經過break、continue、return(若是在函數中的話)提早終止,並拋出異常

2.10.正則表達式

  • 一個事實:JavaScript中的正則表達式很長時間以來基本上沒有什麼變化
  • Unicode標識:在ES6以前,正則表達式只能基於PMB字符匹配,這意味着那些擴展字符會被看成兩個獨立的字符來匹配;在ES6中,u標識符表示正則表達式用Unicode(UTF-16)字符來解釋處理字符串,把這樣的擴展字符串看成單個實體來匹配;影響的是匹配部分的長度;還有一點須要注意,u標識符使得+和*這樣的量詞把整個Unicode碼點做爲單個字符而應用,而不只僅是應用於字符的低位(也就是符號的最右部分);
  • 定點標識:ES6正則表達式中另一個新增的標籤模式是y,一般稱爲「定點(sticky)模式」;定點主要是指在正則表達式的起點有一個虛擬的錨點,只從正則表達式的lastIndex屬性指定的位置開始匹配;
  • 正則表達式flags:ES6以前,想要經過檢查一個正則表達式對象來判斷它應用了哪些標識,須要把它從source屬性的內容中解析出來;在ES6中,如今能夠用新的flags屬性直接獲得這些值;ES6的另外一個調整是若是把標識傳給已有的正則表達式,RegExp(..)構造器如今支持flags;

2.11.數字字面量擴展

  • 雖然能夠用不一樣進制形式指定數字,可是數字的數字值仍是保存的值,而且默認的輸出解釋形式老是十進制

2.12.Unicode

  • Unicode字符範圍從0x0000到0xFFFF,包含可能看到和接觸到的全部(各類語言的)標準打印字符。這組字符稱爲基本多語言平面(BMP)。BMP甚至包含了像雪人這樣的有趣的符號
  • 如今有了能夠用於做Unicode轉義(在字符串和正則表達式中)的新形式,稱爲Unicode碼點轉義
  • 支持Unicode的字符:默認狀況下,JavaScript字符串運算和方法不能感知字符串中俄astral符號;如何精確計算這樣的字符串長度。[...gclef].length
  • 字符定位:原生的ES6對此的答案是charAt,但不支持astral字符的原子性,也不會考慮組合符號的因素;ES6提供了codePointAt;組合String.fromCodePoint和codePointAt來得到支持Unicode的charAt的更簡單且更優的方法;
  • Unicode標識符名

2.13.符號

  • ES6爲JavaScript引入了一個新的原生類型:Symbol。它和其餘原生類型不同,symbol沒有字面量形式
  • 如下幾點須要注意:不能也不該該對Symbol()使用new。它並非一個構造器,也不會建立一個對象;傳給Symbol()的參數是可選的。若是傳入了的話,應該是一個爲這個symbol的用途給出用戶友好描述的字符串;typeof的輸出是一個新的值("symbol"),這是識別symbol的首選方法;
  • 符號自己的內部值-稱爲它的名稱(name)-是不在代碼中出現且沒法得到的。能夠把這個符號值想象爲一個自動生成的、(在應用內部)惟一的字符串值
  • 符號的主要意義是建立一個相似字符串的不會與其餘任何值衝突的值
  • 若是須要把符號值做爲一個真正的字符串使用,那麼它就須要用String()或者toString()進行顯式類型轉換,由於不容許隱式地把符號轉換爲字符串
  • 符號註冊:Symbol.for()在全局符號註冊表中搜索,來查看是否有描述文字相同的符號已經存在,若是有的話就返回它;換句話說,全局註冊表把符號值自己根據其描述文字做爲單例處理;具備諷刺意義的是,基本上符號的目的是爲了取代應用中的magic字符串(賦予特殊意義的任意字符串);可使用Symbol.keyFor()提取註冊符號的描述文本(鍵值);
  • 做爲對象屬性的符號:若是把符號用做對象的屬性/鍵值,那麼它會以一種特殊的方式存儲,使得這個屬性不出如今對這個對象的通常屬性枚舉中;要取得對象的符號屬性,使用Object.getOwnPropertySymbols;

2.14.小結

  • 這些新語法形式中大多數的設計目的都是消除常見編程技巧中的痛點,好比爲函數設定默認值以及把參數的「其他」部分收集到數組中
  • 而像=>箭頭函數這樣的特性看起來是爲了使代碼更簡潔的語法,實際上它有很是特別的行爲特性,應該只在適當的時候使用

第3章 代碼組織

3.1.迭代器

  • 迭代器(iterator)是一個結構化的模式,用於從源以一次一個的方式提取數據
  • 迭代器是一種有序的、連續的、基於拉取的用於消耗數據的組織方式
  • 接口:IteratorResult接口指定了從任何迭代器操做返回的值必須是下面這種形式的對象:{value: .., done: true/false}
  • next()迭代:迭代器的next(..)方法能夠接受一個或多個可選參數。絕大多數內置迭代器沒有利用這個功能
  • 可選的return和throw:多數內置迭代器都沒有實現可選的迭代器接口-return (..)和throw(..);return(..)被定義爲向迭代器發送一個信號,代表消費者代碼已經完畢,不會再從其中提取任何值;throw(..)用於向迭代器報告一個異常/錯誤,迭代器針對這個信號的反應可能不一樣於針對return(..)意味着的完成信號;
  • 自定義迭代器:這種特定的用法強調了迭代器能夠做爲一個模式來組織功能,而不只僅是數據

3.2.生成器

  • ES6引入了一個全新的某種程度上說是奇異的函數形式,稱爲生成器
  • 生成器能夠在執行當中暫停自身,能夠當即恢復執行也能夠過一段時間以後恢復執行。因此顯然它並不像普通函數那樣保證運行到完畢
  • 在執行當中的每次暫停/恢復循環都提供了一個雙向信息傳遞的機會,生成器能夠返回一個值,恢復它的控制代碼也能夠發回一個值
  • 語法:執行生成器,好比foo(5, 10),並不實際在生成器中運行代碼。相反,它會產生一個迭代器控制這個生成器執行其代碼;生成器還有一個能夠在其中使用的新關鍵字,用來標示暫停點:yield;一個永不結束的循環就意味着一個永不結束的生成器,這是徹底有效的,有時候徹底就是你所須要的;yield嚴格上說不是一個運算符,儘管像yield 1這樣使用它的時候確實看起來像是運算符;yield..表達式和賦值表達式行爲上的相似性有必定概念上的合理性;由於yield關鍵字的優先級很低,幾乎yield..以後的任何表達式都會首先計算,而後再經過yield發送;和=賦值同樣,yield也是「右結合」的,也就是說多個yield表達式連續出現等價於用(..)從右向左分組;yield * 能夠調用另一個生成器(經過委託到其迭代器),因此它也是能夠經過調用自身執行某種生成器遞歸;
  • 迭代器控制:ES6以後 的一個早期提案會經過生成器內部一個獨立的元屬性,支持訪問傳入最初next(..)調用的值;能夠把生成器看做是值的產生器,其中每次迭代就是產生一個值來消費;從更通用的意義上來講,可能更合理的角度是把生成器看做一個受控的、可傳遞的代碼執行;
  • 提早完成:生成器上附着的迭代器支持可選的return(..)和throw(..)方法。這兩種方法都有當即終止一個暫停的生成器的效果;return(..)除了能夠手動調用,還能夠在每次迭代的末尾被任何消耗迭代器的ES6構件自動調用,好比for...of循環和spread運算符;這個功能的目的是通知生成器若是控制代碼再也不在它上面迭代,那麼它可能就會執行清理任務(釋放資源,重置狀態等);不要把yield語句放在finally子句內部,雖然這是有效且合法的,但確實是一個可怕的思路。它會延後你的return(..)調用的完成;和return(..)不一樣,迭代器的throw(..)方法歷來不會被自動調用;
  • 錯誤處理:生成器的錯誤處理能夠表達爲try...catch,它能夠在由內向外和由外向內兩個方向工做
  • Transpile生成器:一個練習鞏固了生成器實際上就是狀態機邏輯的簡化語法這個概念
  • 生成器使用:兩個主要模式(產生一系列值;順序執行的任務隊列;)

3.3.模塊

  • 在全部JavaScript代碼中,惟一最重要的代碼組織模式是模塊
  • 舊方法:傳統的模塊模式基於一個帶有內部變量和函數的外層函數,以及一個被返回的「public API」;其中常見的是異步模塊定義(AMD),還有一種是通用模塊定義(UMD);
  • 前進:對於ES6來講,咱們再也不須要依賴於封裝函數和閉包提供模塊支持;ES6模塊和過去咱們處理模塊的方式之間的顯著概念區別;ES6模塊將會爲代碼組織提供完整支持,包括封裝、控制公開API以及引用依賴導入;長遠來看,ES6模塊從本質上說必然會取代以前全部的模塊格式和標準,即便是CommonJS,由於ES6模塊是創建在語言的語法支持基礎上的;模塊transpiler/轉換工具是必不可少的。無論你是在編寫普通模塊、AMD、UMD、CommonJS仍是ES6,這些工具都不得不解析轉化爲對代碼運行的全部環境都適用的形式;
  • 新方法:支撐ES6模塊的兩個主要新關鍵字是import和export;import和export都必須出如今使用它們的最頂層做用域。它們必須出如今全部代碼塊和函數的外面;沒有用export標示的一切都在模塊做用域內部保持私有。在模塊內沒有全局做用域;模塊還能訪問window和全部的「全局」變量,只是不做爲詞法上的頂層做用域;模塊導出不是像你熟悉的賦值運算符=那樣只是值或者引用的普通賦值。實際上,導出的是對這些東西(變量等)的綁定(相似於指針);默認導出把一個特定導出綁定設置爲導入模塊時的默認導出。綁定的名稱就是default;每一個模塊只能有一個default;JavaScript引擎沒法靜態分析平凡對象的內容,這意味着它沒法對靜態import進行性能優化。讓每一個成員獨立且顯式地導出的優勢是引擎能夠對其進行靜態分析和優化;
  • 模塊依賴環:首先必須聲明,儘可能避免故意設計帶有環形依賴的系統;本質上說,相互導入,加上檢驗兩個import語句的有效性的靜態驗證,虛擬組合了兩個獨立的模塊空間(經過綁定),這樣foo(..)能夠調用bar(..),反過來也是同樣;import語句的靜態加載語義意味着能夠確保經過import相互依賴的「foo」和「bar」在其中任何一個運行以前,兩者都會被加載、解析和編譯;
  • 模塊加載:import語句使用外部環境(瀏覽器、Node.js等)提供的獨立機制,來實際把模塊標識符字符串解析成可用的指令,用於尋找和加載所需的模塊。這個機制就是系統模塊加載器;若是在瀏覽器中,環境提供的默認模塊加載器會把模塊標識符解析爲URL。若是在像Node.js這樣的服務器上就解析爲本地文件系統路徑;模塊加載器自己不是由ES6指定的。他是獨立的、平行的標準,目前由WHATWG瀏覽器標準工做組管理;

3.4.類

  • class:新的ES6類機制的核心是關鍵字class,表示一個塊,其內容定義了一個函數原型的成員;ES6 class 自己並非一個真正的實體,而是一個包裹着其餘像函數和屬性這樣的具體實體並把他們組合到一塊兒的元概念;
  • extends和super:ES6還經過面向類的經常使用術語extends提供了一個語法糖,用來在兩個函數原型之間創建[[Prototype]]委託連接-一般被誤稱爲「繼承」或者使人迷惑的標識爲「原型繼承」;在構造器中,super自動指向「父構造器」,在方法中,super會指向「父對象」,這樣就能夠訪問其屬性/方法了;
  • new.target:新概念,稱爲元屬性;new target是一個新的在全部函數中均可用的「魔法」值,儘管在通常函數中它一般是undefined;在任何構造器中,new.target老是指向new實際上直接調用的構造器,即便構造器是在父類中且經過子類構造器用super(..)委託調用;除了訪問靜態/方法以外,類構造器中的new.target元屬性沒有什麼其餘用處;
  • static:直接添加到這個類的函數對象上,而不是在這個函數對象的prototype對象上

3.5.小結

  • 迭代器提供了對數組或運算的順序訪問。能夠經過像for..of...這些新語言特性來消耗迭代器
  • 生成器是支持本地暫停/恢復的函數。經過迭代器來控制。它們能夠用於編程(也是交互地,經過yield/next消息傳遞)生成供迭代消耗的值
  • 模塊支持對實現細節的私有封裝,並提供公開導出API。模塊定義是基於文件的單例實例,在編譯時靜態決議
  • 類對基於原型的編碼提供了更清晰的語法。新增的super也解決了[[prototype]]鏈中相對引用的棘手問題

第4章 異步流控制

  • 管理異步的主要機制一直以來都是函數回調
  • ES6增長了一個新的特性來幫助解決只用回調實現異步的嚴重缺陷:Promise

4.1.Promise

  • 介紹:一些錯誤觀念:Promise不是對回調的替代。Promise在回調代碼和將要執行這個任務的異步代碼之間提供了一種可靠的中間機制來管理回調;能夠把Promise連接到一塊兒,這就把一系列異步完成的步驟串聯了起來;Promise鏈提供了一個近似的異步控制流;還有一種定義Promise的方式,就是把它看做一個將來值,對一個值得獨立於時間的封裝容器;換句話說,Promise能夠被看做是同步函數返回值的異步版本;Promise的決議結果只有兩種可能:完成或拒絕,附帶一個可選的單個值;Promise只能被決議(完成或者拒絕)一次;
  • 構造和使用Promise:提供給構造器Promise(..)的兩個參數都是函數,通常稱爲resolve(..)和reject(..);Promise有一個then(..)方法,接受一個或兩個回調函數做爲參數;默認的成功回調把完成值傳出,默認的出錯回調會傳遞拒絕緣由值;若是永遠不經過then(..)或catch(..)調用來觀察的話,它就會一直保持未處理狀態;
  • Thenable:Promise(..)構造器的真正實例是Promise。但還有一些類promise對象,稱爲thenable,通常來講,它們也能夠用Promise機制解釋;任何提供了then(..)函數的對象(或函數)都被認爲是thenable;通常來講,若是從某個其餘系統接收到一個自稱promise或者thenable的東西,不該該盲目信任它;避免把可能被誤認爲thenable的值直接用於Promise機制;
  • Promise API:Promise API還提供了一些靜態方法與Promise一塊兒工做;Promise.resolve(..)建立了一個決議到傳入值的promise;resolve(..)和Promise.resolve(..)能夠接受promise並接受它的狀態/決議,而reject(..)Promise.reject(..)並不區分接收的值是什麼;Promise.all([..])等待全部都完成(或者第一個拒絕),而Promise.race([..])等待第一個完成或者拒絕;Promise.all([])將會當即完成(沒有完成值),Promise.race([])將會永遠掛起。所以建議,永遠不要用空數組使用這些方法;

4.2.生成器+Promise

  • 這個重要的模式須要理解一下:生成器能夠yield一個promise,而後這個promise能夠被綁定,用其完成值來恢復這個生成器的運行
  • Promise是一種把普通回調或者chunk控制反轉反轉回來的可靠系統
  • 把Promise的可信任性與生成器的同步代碼組合在一塊兒有效解決了回調全部的重要缺陷
  • 本質上說,只要代碼中出現超過兩個異步步驟的流控制邏輯,均可以也應該使用由run工具驅動的promise-yield生成器以異步風格表達控制流

4.3.小結

  • ES6新增了Promise來彌補回調的主要缺陷之一:缺乏對可預測行爲方式的保證。Promise表明了來自可能異步的任務的將來完成值,跨越同步和異步邊界對行爲進行規範化
  • Promise與生成器的結合徹底實現了從新安排異步流控制代碼來消除醜陋的回調亂燉(或稱地獄)

第5章 集合

  • Map就像是一個對象(鍵/值對),可是鍵值並不是只能爲字符串,而是可使用任何值-甚至是另外一個對象或map
  • Set與數組(值的序列)相似,可是其中的值是惟一的;若是新增的值是重複的,就會被忽略。
  • 還有相應的弱(與內存/垃圾回收相關)版本:WeakMap和WeakSet

5.1.TypedArray

  • 實際上帶類型的數組更可能是爲了使用類數組語義(索引訪問等)結構化訪問二進制數據
  • 大小端:理解下面這點很重要:arr的映射是按照運行JavaScript的平臺的大小端設置(大端或小端)進行的;大小端的意思是多字節數字(好比前面代碼片斷中建立的16位無符號整型)中低字節(8位)位於這個數字字節表示中德右側仍是左側;目前Web上最經常使用的是小端表示方式,可是確定存在不採用這種方式的瀏覽器;
  • 多視圖:單個buffer能夠關聯多個視圖
  • 帶類數組構造器:帶類型數組構造器的實例幾乎和普通原生數組徹底同樣。一些區別包括具備固定的長度以及值都屬於某種「類型」;不能對TypedArray使用不合理的Array.prototype方法,好比修改器(splice(..)push(..)等)和concat(..);要清楚TypedArray中德元素是限制在聲明的位數大小中的;要解決平方值溢出的侷限,可使用TypedArray.from(..)函數;

5.2.Map

  • 瞭解對象是建立無序鍵/值對數據結構,也稱爲映射(map)的主要機制
  • 對象做爲映射的主要缺點是不能使用非字符串值做爲鍵
  • 惟一的缺點就是不能使用方括號[ ]語法設置和獲取值,但徹底可使用get(..)set(..)方法完美代替
  • Map值
  • Map鍵:map的本質是容許你把某些額外的信息(值)關聯到一個對象(鍵)上,而無需把這個信息放入對象自己;對於map來講,儘管可使用任意類型的值做爲鍵,但一般咱們會使用對象,由於字符串或者其餘基本類型已經能夠做爲普通對象的鍵使用;

5.3.WeakMap

  • WeakMap是map的變體,兩者的多數外部行爲都是同樣的,區別在於內部內存分配(特別是其GC)的工做方式
  • WeakMap(只)接受對象做爲鍵。這些對象是被弱持有的,也就是說若是對象自己被垃圾回收的話,在WeakMap中的這個項目也會被移除
  • WeakMap沒有size屬性或clear()方法,也不會暴露任何鍵、值或項目上的迭代器
  • 須要注意的是,WeakMap只是弱持有它的鍵,而不是值

5.4.Set

  • set是一個值的集合,其中的值惟一(重複會被忽略)
  • 除了會把-0和0看成是同樣的而不加區別以外,has(..)中的比較算法和Object.is(..)幾乎同樣
  • Set迭代器:set固有的惟一性是它最有用的特性;set的惟一性不容許強制轉換,因此1和「1」被認爲是不一樣的值;

5.5.WeakSet

  • 就像WeakMap弱持有它的鍵(對其值是強持有的)同樣,WeakSet對其值也是弱持有的(這裏並無鍵)
  • WeakSet的值必須是對象,而並不像set同樣能夠是原生類型值

5.6.小結

  • TypedArray提供了對二進制數據buffer的各類整型類型「視圖」,好比8位無符號整型和32位浮點型。對二進制數據的數組訪問使得運算更容易表達和維護,從而能夠更容易操縱視頻、音頻、canvas數據等這樣的複雜數據
  • Map是鍵/值對,其中的鍵不僅是字符串/原生類型,也能夠是對象。Set是成員值(任意類型)惟一的列表
  • WeakMap也是map,其中的鍵(對象)是弱持有的,所以當它是對這個對象的最後一個引用的時候,GC(垃圾回收)能夠回收這個項目。WeakSet也是set,其中的值是弱持有的,也就是若是其中的項目是對這個對象最後一個引用的時候,GC能夠移除它

第6章 新增API

6.1.Array

  • 各類JavaScript用戶庫擴展最多的特性之一就是數組(Array)類型
  • 1.靜態函數Array.of(..):Array(..)構造器有一個衆所周知的陷阱,就是若是隻傳入一個參數,而且這個參數是數字的話,那麼不會構造一個值爲這個數字的單個元素的數組,而是構造一個空數組,其length屬性爲這個數字
  • 2.靜態函數Array.from(..):JavaScript中的「類(似)數組對象」是指一個有length屬性,具體說是大於等於0的整數值的對象;廣泛的需求就是把他們轉爲真正的數組,Array.prototype.slice.call(arrLike);新的ES6 Array.from(..)方法都是更好理解、更優雅、更簡潔的替代方法;
  • 3.建立數組和子類型:of(..)和from(..)都使用訪問它們的構造器來構造數組
  • 4.原型方法copyWidthin(..):Array.copyWithin(..)是一個新的修改器方法,全部數組都支持;它從一個數組中複製一部分到同一個數組的另外一個位置,覆蓋這個位置全部原來的值;copyWithin(..)方法不會增長數組的長度。到達數組結尾複製就會中止;
  • 5.原型方法fill(..):用指定值徹底(或部分)填充已存在的數組;可選接收參數start和end,它們指定了數組要填充的子集位置;
  • 6.原型方法find(..):在數組中搜索一個值得最經常使用方法一直是indexOf(..)方法,這個方法返回找到值的索引,若是沒有找到就返回-1;indexOf(..)須要嚴格匹配===;ES6的find(..)一旦回到返回true/真值,會返回實際的數組值;
  • 7.原型方法findIndex(..):若是須要嚴格匹配的索引值,那麼使用indexOf(..),若是須要自定義匹配的索引值,那麼使用findIndex(..)
  • 8.原型方法entries()、values()、keys():它提供了一樣的迭代器方法entries()、values()和keys(),從這個意義上說,他是一個集合

6.2.Object

  • 1.靜態函數Object.is(..):靜態函數Object.is(..)執行比===比較更嚴格的值比較;Object.is(..)調用底層的SameValue算法;若是須要嚴格識別NaN或者-0,那麼應該選擇Object.is(..);
  • 2.靜態函數Object.getOwnPropertySymbols(..):它直接從對象上取得全部的符號屬性
  • 3.靜態函數Object.setPrototypeOf(..):設置對象的[[Prototype]]用於行爲委託
  • 4.靜態函數Object.assign(..):不少JavaScript庫/框架提供了用於把一個對象的屬性複製/混合到另外一個對象中德工具;ES6新增了Object.assign(..),對於每一個源來講,它的可枚舉和本身擁有的(也就是否是「繼承來的」)鍵值,包括符號都會經過簡單=賦值被複制;不可枚舉的屬性和非自有的屬性都被排除在賦值過程以外;Object.create(..)是ES5工具,建立一個[[Prototype]]連接的空對象;

6.3.Math

  • 更優化地執行這些計算,或者執行比手動版本精度更高的計算

6.4.Number

  • 兩個新增內容就是指向已有的全局函數的引用:Number.parseInt(..)和Number.parseFloat(..)
  • 1.靜態屬性:新增了一些輔助數字常量:Number.EPSOLON、Number.MAX_SAFE_INTEGER、Number.MIN_SAFE_INTEGER
  • 2.靜態屬性Number.isNaN(..):標準全局工具isNaN(..)自出現以來就是有缺陷的,它對非數字的東西都返回true,而不是隻對真實的NaN值返回true,由於它把參數強制轉換爲數字類型(可能會錯誤地致使NaN)。ES6增長了一個修正工具Number.isNaN,能夠按照指望工做
  • 3.靜態屬性Number.isFinite(..):標準的全局isFinite(..)會對參數進行強制類型轉換,可是Number.isFinite(..)會略去這種強制行爲
  • 4.整型相關靜態函數:JavaScript的數字值永遠都是浮點數(IEE-754);檢查一個值得小數部分是否非0,x===Math.floor(x),ES6新增了Number.isInteger(..);JavaScript代碼自己不會由於只使用整數而運行得更快,可是,只有在使用整數時,引擎才能夠採用優化技術(好比asm.js);

6.5.字符串

  • 1.Unicode函數:String.fromCodePoint(..)、String.codePointAt(..)和String.normalize(..)新增這些函數是爲了提升JavaScript字符串值uiUnicode的支持;字符串原型方法normalize(..)用於執行Unicode規範化,或者把字符用「合併符」鏈接起來或者把合併的字符解開;
  • 2.靜態函數String.raw(..):String.raw(..)工具做爲內置標籤函數提供,與模板字符串字面值一塊兒使用,用於得到不該用任何轉義序列的原始字符串
  • 3.原型函數repeat(..):重複字符串
  • 4.字符串檢查函數:又新增了3個用於搜索/檢查的新方法:startsWidth、endsWidth和includes

第7章 元編程

寫在前面

  • 元編程是指操做目標是程序自己的行爲特性的編程。換句話說,它是程序的編程的編程
  • 元編程關注如下一點或幾點:代碼查看自身,代碼修改自身,代碼修改默認語言特性,以便影響其餘代碼
  • 元編程的目標是利用語言自身的內省能力是代碼的其他部分更具描述性、表達性和靈活性

7.1.函數名稱

  • 默認狀況下,name屬性不可寫,但可配置,也就是說若是須要的話,可以使用Object.defineProperty(..)來手動修改

7.2.元屬性

  • 元屬性以屬性訪問的形式提供特殊的其餘方法沒法獲取的元信息
  • 對於全部的元編程技術都要當心,不要編寫過於機靈的代碼,讓將來的你或者其餘代碼維護者難以理解

7.3.公開符號

  • 除了在本身的程序中定義符號以外,JavaScript預先定義了一些內置符號,稱爲公開符號
  • Symbol.iterator表示任意對象上的一個專門位置(屬性),語言機制自動在這個位置上尋找一個方法,這個方法構造一個迭代器來消耗這個對象的值
  • 最多見的一個元編程任務,就是在一個值上進行內省來找出它是什麼種類,這一般是爲了肯定其上適合執行何種運算。最經常使用的內省技術是toString()和instanceof
  • 符號@@species,這個符號控制要生成新實例時,類的內置方法使用哪個構造器
  • 抽象類型轉換運算ToPrimitive,它用在對象爲了某個操做必須被強制轉換爲一個原生類型值得時候。ES6以前,沒有辦法控制這個行爲
  • ES6中,在任意對象值上做爲屬性的符號@@toPrimitivesymbol均可以經過指定一個方法來定製這個ToPrimitive強制轉換
  • 對於正則表達式對象,有4個公開符號能夠被覆蓋:@@match、@@search、@@split、@@replace
  • 符號@@isConcatSpreadable能夠被定義爲任意對象的布爾型屬性(Symbol.isConcattSpreadable),用來指示若是把它傳給一個數組的concat(..)是否應該將其展開
  • 符號@@unscopables能夠被定義爲任意對象的對象屬性(Symbol.unscopables),用來指示使用with語句時哪些屬性能夠或部能夠暴露爲詞法變量
  • strict模式下不容許with語句,所以應當被認爲是語言的過期特性。不要使用它

7.4.代理

  • ES6種新增的最明顯的元編程特性之一是Proxy(代理)特性
  • 代理是一種由你建立的特殊的對象,它「封裝」另外一個普通對象--或者說擋在這個普通對象的前面
  • 代理侷限性:能夠在對象上執行的很普遍的一組基本操做均可以經過這些元編程處理函數trap;代理處理函數總會有一些不變形(invariant),亦即不能被覆蓋的行爲;這些不變性限制了自定義代理行爲的能力,但它們的目的只是爲了防止你建立詭異或罕見(或者不一致)的行爲;
  • 可取消代理:想要建立一個在你想要中止它做爲代理時即可以被停用的代理。解決方案是建立可取消代理;可取消代理用Proxy.revocable(..)建立,這是一個普通函數,而不像Proxy(..)同樣是構造器;一旦可取消代理被取消,任何對它的訪問(觸發它的任意trap)都會拋出TypeError;可取消代理的一個可能應用場景是,在你的應用中把代理分發到第三方,其中管理你的模型數據,而不是給出真實模型自己的引用;
  • 使用代理:代理成爲了代碼交互的主要對象,而實際目標對象保持隱藏/被保護的狀態

7.5.Reflect API

  • Reflect對象是一個平凡對象(就像Math),不像其餘內置原生值同樣是函數/構造器
  • 它持有對應於各類可控的元編程任務的靜態函數
  • 有一個區別是若是第一個參數(目標對象)不是對象的話,Object.*相應工具會試圖把它類型轉換爲一個對象
  • Reflect的元編程能力提供了模擬各類語法特性的編程等價物,把以前隱藏的抽象操做暴露出來。能夠利用這些能力擴展功能和API,以實現領域特定語言(DSL)
  • 屬性排序:在ES6以前,一個對象鍵/屬性的列出順序是依賴於具體實現,並未在規範中定義;對於ES6來講,擁有屬性的列出順序是是由[[OwnPropertyKeys]]算法定義的;其順序爲(首先,按照數字上升排序,枚舉全部整數索引擁有的屬性;而後,按照建立順序枚舉其他的擁有的字符串屬性名;最後,按照建立順序枚舉擁有的符號屬性;)

7.6.特性測試

  • 特性測試,就是一種由你運行的用來判斷一個特性是否可用的測試
  • JavaScript中最經常使用的特性測試是檢查一個API是否存在,若是不存在的話,定義一個polyfill

7.7.尾遞歸調用

  • 一般,在一個函數內部調用另外一個函數的時候,會分配第二個棧幀來獨立管理第二個函數調用的變量/函數
  • 當考慮遞歸編程的時候(一個函數重複調用自身)-或者兩個或多個函數彼此調用造成遞歸-調用棧的深度很容易達到成百上千,甚至更多
  • JavaScript引擎不得不設置一個武斷的限制來防止這種編程技術引發瀏覽器和設備內存消耗盡而崩潰。「RangeError:Maximum call stack size exceeded」
  • 調用棧深度限制不禁規範控制。它是依賴於具體實現的,而且根據瀏覽器和設備不一樣而有所不一樣
  • 尾調用優化(Tail Call Optimization, TCO) - 因而額外的棧幀分配是不須要的。引擎不須要對下一個函數調用建立一個新的棧幀,只需複用已有的棧幀

第8章 ES6以後

  • 在新特性尚未被須要支持的全部瀏覽器都實現的狀況下,transpiler和polyfill是咱們遷移到新特性的橋樑

8.1.異步函數

  • async function 本質上就是生成器+Promise+run(..)模式的語法糖;它們底層的運做方式是同樣的
  • 警告:async function有一個沒有解決的問題,由於它只返回一個promise,因此沒有辦法從外部取消一個正在運行的async function實例;Promise是不可取消的(至少在編寫本部分的時候是如此);

8.2.Object.observe

  • 可能在後ES6,咱們將會看到經過工具Object.observe(..)直接添加到語言中的支持。
  • 本質上說,這個思路就是你能夠創建一個偵聽者(listener)來觀察對象的改變,而後在每次變化發生時調用一個回調
  • 能夠觀察的改變有6種類型:add、update、delete、reconfigure、setPrototype、preventExtensions
  • 1.自定義改變事件:除了前面6類內置改變事件,也能夠偵聽和發出自定義改變事件
  • 2.結束觀測

8.3.冪運算符

  • 有提案提出爲JavaScript新增一個運算符用於執行冪運算,就像Math.pow(..)同樣

8.4.對象屬性與...

  • ...運算符展開和收集數組的用法很直觀,那麼對於對象呢

8.5.Array#includes

  • 出現了一個得到了大量支持的提案,提出增長一個真正返回布爾值的數組搜索方法includes(..)
  • Array.includes(..)使用的匹配邏輯可以找到Nan值,可是沒法區分-0和0

8.6.SIMD

  • SIMD API暴露了能夠同時對多個數字值運算的各類底層(CPU)指令

8.7.WebAssembly(WASM)

  • 最近(以及不久的未來)JavaScript語言設計修改上的最大壓力之一就是須要成爲更適合從其餘語言(好比C/C++、ClojureScript)變換/交叉編譯的目標語言
  • ASM.js是合法JavaScript的一個子集,這個子集最嚴格地限制了那些使得JavaScript引擎難以優化的行爲。結果就是兼容ASM.js的代碼運行在支持ASM的引擎上時效率有巨大的提高,幾乎與原生優化的等價C程序至關
  • WASM提出了一種代碼的高度壓縮AST(語法樹)二進制表示格式,而後能夠直接向JavaScript引擎發出指令,而它的基礎結構,不須要經過JavaScript解析,甚至不須要符合JavaScript的規則

寫在後面

相關文章
相關標籤/搜索