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

来源:https://www.cnblogs.com/yzsunlei/archive/2020/05/17/12903042.html
-Advertisement-
Play Games

寫在前面 書籍介紹:JavaScript這門語言簡單易用,很容易上手,但其語言機制複雜微妙,即使是經驗豐富的JavaScript開發人員,如果沒有認真學習的話也無法真正理解。本套書直面當前JavaScript開發人員不求甚解的大趨勢,深入理解語言內部的機制,全面介紹了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的規則

寫在後面


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • BIOS 中英文對照表 轉載於https://www.dell.com/support/article/zh-cn/sln262122/bios-%E4%B8%AD%E8%8B%B1%E6%96%87%E5%AF%B9%E7%85%A7%E8%A1%A8?lang=zh 註意:機型不同 BIOS 版 ...
  • Redis 系列: 1. "Redis系列(一)Redis入門" 2. "Redis系列(二)Redis的8種數據類型" 3. "Redis系列(三)Redis的事務和Spring Boot整合" 4. "Redis系列(四)Redis配置文件和持久化" 5. "Redis系列(五)發佈訂閱模式、主 ...
  • 假設有一個用戶表 user,數據如下: 1、查詢表中 uid 重覆的數據 SELECT id, uid, name FROM USER WHERE uid IN (SELECT uid FROM USER GROUP BY uid HAVING COUNT(uid) > 1); 2、查詢表中重覆數據 ...
  • 在SQL Server 2017的錯誤日誌中出現"Parallel redo is started for database 'xxx' with worker pool size [2]"和“Parallel redo is shutdown for database 'xxx' with wor... ...
  • Redis 伺服器將所有的資料庫都保存在伺服器狀態redisServer結構的db數組中,db數組的每個項都是一個redisDB: struct redisServer{ //一個數組保存著伺服器中的所有資料庫 redisDb *db; //資料庫的個數 int dbnum; } dbnum:伺服器 ...
  • 一、使用碎片來進行一個最佳實踐,即我們寫一個新聞的app 1.首先先建立一個新聞類 package com.example.fragmentbestpractice; ​ public class News { private String title; private String content ...
  • 本文章將記錄Objective-C中消息傳遞和轉發機制、Method Swizzling的相關資料,如有錯誤歡迎指出~ Objective-C 本質上是一種基於 C 語言的領域特定語言。C 語言是一門靜態語言,其在編譯時決定調用哪個函數。而 Objective-C 則是一門動態語言,其在編譯時不能決 ...
  • "來源掘金小冊 react實戰:設計模式和最佳實踐" react設計思想 1. 屬性展開 保留當前組件需要的props,並且使其他的props傳遞下去 2. 在react中,界面完全由數據驅動 3. 在react中,一切都是組件 4. props是react組件之間通訊的基本方式 設計react組件 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...