前言
第三次閱讀阮一峰老師的《ES6標準入門》了,以前閱讀時不細心,很多地方都是一目十行。最近這次閱讀都是逐個逐個字來讀,發現很多以前都沒有注意到的知識點,為了方便記憶和預覽全部ES6特性,所以寫下本文。
以下提到的《ES6標準入門》統一使用《ES6》這個名稱來代替,而最新的ES6版本也是截止到當前的ES2020
本文的知識點完全是參考或摘錄《ES6》裡的語句,有部分語句為了方便理解和記憶,進行了相同意思的轉義,同時對知識點進行歸類劃分。為了讓大家能集中精力來記住這些特性,全文一句廢話和題外話都沒有,全部模塊以筆記的形式進行書寫,如果看得不是很慣建議對照《ES6》的內容來學習。
本文整理出來的筆記都是書中的精華內容,囊括了整個ES6體系的所有特性,非常方便大家重新認識全部ES6特性。半小時的閱讀就可對ES6有一個全面的瞭解,可認為是一本ES6特性小字典,收藏後可隨時查閱。即使看不完也要拉到本文末尾喔,有個大彩蛋,嘻嘻!
修正
ES6是ECMA為JavaScript制定的第6個標準版本,相關歷史可查看此章節《ES6-ECMAScript6簡介》。
標準委員會最終決定,標準在每年6月正式發佈並作為當年的正式版本,接下來的時間裡就在此版本的基礎上進行改動,直到下一年6月草案就自然變成新一年的版本,這樣一來就無需以前的版本號,只要用年份標記即可。ECMAscript 2015是在2015年6月發佈ES6的第一個版本。以此類推,ECMAscript 2016是ES6的第二個版本、 ECMAscript 2017是ES6的第三個版本。ES6既是一個歷史名詞也是一個泛指,含義是5.1版本以後的JavaScript下一代標準,目前涵蓋了ES2015、ES2016、ES2017、ES2018、ES2019、ES2020。
所以有些文章上提到的ES7(實質上是ES2016)、ES8(實質上是ES2017)、ES9(實質上是ES2018)、ES10(實質上是ES2019)、ES11(實質上是ES2020),實質上都是一些不規範的概念。從ES1到ES6,每個標準都是花了好幾年甚至十多年才制定下來,你一個ES6到ES7,ES7到ES8,才用了一年,按照這樣的定義下去,那不是很快就ES20了。用正確的概念來說ES6目前涵蓋了ES2015、ES2016 、ES2017、ES2018、ES2019、ES2020。
另外,ES6更新的內容主要分為以下幾點
- 表達式:聲明、解構賦值
- 內置對象:字符串擴展、數值擴展、對象擴展、數組擴展、函數擴展、正則擴展、Symbol、Set、Map、Proxy、Reflect
- 語句與運算:Class、Module、Iterator
- 異步編程:Promise、Generator、Async
ES2015
- const命令:聲明常量
- let命令:聲明變量
作用
- 作用域 全局作用域 函數作用域:function() {} 塊級作用域:{}
- 作用範圍 var命令在全局代碼中執行 const命令和let命令只能在代碼塊中執行
- 賦值使用 const命令聲明常量後必須立馬賦值 let命令聲明變量後可立馬賦值或使用時賦值
- 聲明方法:var、const、let、function、class、import
重點難點
- 不允許重複聲明
- 未定義就使用會報錯:const命令和let命令不存在變量提升
- 暫時性死區:在代碼塊內使用const命令和let命令聲明變量之前,該變量都不可用
解構賦值
- 字符串結構:const [a, b, c, d, e] = "hello"
- 數值結構:const { toString: s } = 123
- 布爾解構:const { toString: b } = true
- 對象結構 形式:const { x, y } = { x: 1, y: 2 } 默認:const { x, y = 2 } = { x: 1 } 改名:const { x, y: z } = { x: 1, y: 2 }
- 數組結構 規則:數據結構具有Iterator接口可採用數組形式的解構賦值 形式:const [x, y] = [1, 2] 默認:const [x, y = 2] = [1]
- 函數參數結構 數組結構:function Func([x = 0, y = 1]) {} 對象結構:function Func({ x = 0, y = 1 } = {}) {}
應用場景
- 交換變量值:[x, y] = [y, x]
- 返回函數多個值:const [x, y, z] = Func()
- 定義函數參數:Func([1, 2])
- 提取JSON數據:const { name, version } = packageJson
- 定義函數參數默認值:function Func({ x = 1, y = 2 } = {}) {}
- 遍歷Map結構:for (let [k, v] of Map) {}
- 輸入模塊指定屬性和方法:const { readFile, writeFile } = require("fs")
重點難點
- 匹配模式:只要等號兩邊的模式相同,左邊的變量就會被賦予對應的值
- 解構賦值規則:只要等號右邊的值不是對象或數組,就先將其轉為對象
- 解構默認值生效條件:屬性值嚴格等於undefined
- 解構遵循匹配模式
- 解構不成功時變量的值等於undefined
- undefined和null無法轉為對象,因此無法進行解構
字符串擴展
- Unicode表示法:大括號包含表示Unicode字符(\\\\u{0xXX}或\\\\u{0XXX})
- 字符串遍歷:可通過for-of遍歷字符串
- 字符串模板:可單行可多行可插入變量的增強版字符串
- 標籤模板:函數參數的特殊調用
- String.raw():返回把字符串所有變量替換且對斜槓進行轉義的結果
- String.fromCodePoint():返回碼點對應字符
- codePointAt():返回字符對應碼點(String.fromCodePoint()的逆操作)
- normalize():把字符的不同表示方法統一為同樣形式,返回新字符串(Unicode正規化)
- repeat():把字符串重複n次,返回新字符串
- matchAll():返回正則表達式在字符串的所有匹配
- includes():是否存在指定字符串
- startsWith():是否存在字符串頭部指定字符串
- endsWith():是否存在字符串尾部指定字符串
重點難點
- 以上擴展方法均可作用於由4個字節儲存的Unicode字符上
數值擴展
- 二進制表示法:0b或0B開頭表示二進制(0bXX或0BXX)
- 八進制表示法:0o或0O開頭表示二進制(0oXX或0OXX)
- Number.EPSILON:數值最小精度
- Number.MIN_SAFE_INTEGER:最小安全數值(-2^53)
- Number.MAX_SAFE_INTEGER:最大安全數值(2^53)
- Number.parseInt():返回轉換值的整數部分
- Number.parseFloat():返回轉換值的浮點數部分
- Number.isFinite():是否為有限數值
- Number.isNaN():是否為NaN
- Number.isInteger():是否為整數
- Number.isSafeInteger():是否在數值安全範圍內
- Math.trunc():返回數值整數部分
- Math.sign():返回數值類型(正數1、負數-1、零0)
- Math.cbrt():返回數值立方根
- Math.clz32():返回數值的32位無符號整數形式
- Math.imul():返回兩個數值相乘
- Math.fround():返回數值的32位單精度浮點數形式
- Math.hypot():返回所有數值平方和的平方根
- Math.expm1():返回e^n - 1
- Math.log1p():返回1 + n的自然對數(Math.log(1 + n))
- Math.log10():返回以10為底的n的對數
- Math.log2():返回以2為底的n的對數
- Math.sinh():返回n的雙曲正弦
- Math.cosh() :返回n的雙曲餘弦
- Math.tanh():返回n的雙曲正切
- Math.asinh():返回n的反雙曲正弦
- Math.acosh():返回n的反雙曲餘弦
- Math.atanh():返回n的反雙曲正切
對象擴展
- 簡潔表示法:直接寫入變量和函數作為對象的屬性和方法({ prop, method() {} })
- 屬性名錶達式:字面量定義對象時使用[]定義鍵([prop],不能與上同時使用)
- 方法的name屬性:返回方法函數名 取值函數(getter)和存值函數(setter):get/set 函數名(屬性的描述對象在get和set上) bind返回的函數:bound 函數名 Function構造函數返回的函數實例:anonymous
- 屬性的可枚舉性和遍歷:描述對象的enumerable
- super關鍵字:指向當前對象的原型對象(只能用在對象的簡寫方法中method() {})
- Object.is():對比兩值是否相等
- Object.assign():合併對象(淺拷貝),返回原對象
- Object.getPrototypeOf():返回對象的原型對象
- Object.setPrototypeOf():設置對象的原型對象
- __proto__:返回或設置對象的原型對象
屬性遍歷
- 描述:自身、可繼承、可枚舉、非枚舉、Symbol
- 遍歷 for-in:遍歷對象自身可繼承可枚舉屬性 Object.keys():返回對象自身可枚舉屬性鍵組成的數組 Object.getOwnPropertyNames():返回對象自身非Symbol屬性鍵組成的數組 Object.getOwnPropertySymbols():返回對象自身Symbol屬性鍵組成的數組 Reflect.ownKeys():返回對象自身全部屬性鍵組成的數組
- 規則 首先遍歷所有數值鍵,按照數值升序排列 其次遍歷所有字符串鍵,按照加入時間升序排列 最後遍歷所有Symbol鍵,按照加入時間升序排列
數組擴展
- 擴展運算符(...):轉換數組為用逗號分隔的參數序列([...arr],相當於rest/spread參數的逆運算)
- Array.from():轉換具有Iterator接口的數據結構為真正數組,返回新數組 類數組對象:包含length的對象、Arguments對象、NodeList對象 可遍歷對象:String、Set結構、Map結構、Generator函數
- Array.of():轉換一組值為真正數組,返回新數組
- copyWithin():把指定位置的成員複製到其他位置,返回原數組
- find():返回第一個符合條件的成員
- findIndex():返回第一個符合條件的成員索引值
- fill():根據指定值填充整個數組,返回原數組
- keys():返回以索引值為遍歷器的對象
- values():返回以屬性值為遍歷器的對象
- entries():返回以索引值和屬性值為遍歷器的對象
- 數組空位:ES6明確將數組空位轉為undefined(空位處理規不一,建議避免出現)
擴展應用
- 克隆數組:const arr = [...arr1]
- 合併數組:const arr = [...arr1, ...arr2]
- 拼接數組:arr.push(...arr1)
- 代替apply:Math.max.apply(null, [x, y]) => Math.max(...[x, y])
- 轉換字符串為數組:[..."hello"]
- 轉換類數組對象為數組:[...Arguments, ...NodeList]
- 轉換可遍歷對象為數組:[...String, ...Set, ...Map, ...Generator]
- 與數組解構賦值結合:const [x, ...rest/spread] = [1, 2, 3]
- 計算Unicode字符長度:Array.from("hello").length => [..."hello"].length
重點難點
- 使用keys()、values()、entries()返回的遍歷器對象,可用for-of自動遍歷或next()手動遍歷
函數擴展
- 參數默認值:為函數參數指定默認值 形式:function Func(x = 1, y = 2) {} 參數賦值:惰性求值(函數調用後才求值) 參數位置:尾參數 參數作用域:函數作用域 聲明方式:默認聲明,不能用const或let再次聲明 length:返回沒有指定默認值的參數個數 與解構賦值默認值結合:function Func({ x = 1, y = 2 } = {}) {} 應用 指定某個參數不得省略,省略即拋出錯誤:function Func(x = throwMissing()) {} 將參數默認值設為undefined,表明此參數可省略:Func(undefined, 1)
- rest/spread參數(...):返回函數多餘參數 形式:以數組的形式存在,之後不能再有其他參數 作用:代替Arguments對象 length:返回沒有指定默認值的參數個數但不包括rest/spread參數
- 嚴格模式:在嚴格條件下運行JS 應用:只要函數參數使用默認值、解構賦值、擴展運算符,那麼函數內部就不能顯式設定為嚴格模式
- name屬性:返回函數的函數名 將匿名函數賦值給變量:空字符串(ES5)、變量名(ES6) 將具名函數賦值給變量:函數名(ES5和ES6) bind返回的函數:bound 函數名(ES5和ES6) Function構造函數返回的函數實例:anonymous(ES5和ES6)
- 箭頭函數(=>):函數簡寫 無參數:() => {} 單個參數:x => {} 多個參數:(x, y) => {} 解構參數:({x, y}) => {} 嵌套使用:部署管道機制 this指向固定化 並非因為內部有綁定this的機制,而是根本沒有自己的this,導致內部的this就是外層代碼塊的this 因為沒有this,因此不能用作構造函數
- 尾調用優化:只保留內層函數的調用幀 尾調用 定義:某個函數的最後一步是調用另一個函數 形式:function f(x) { return g(x); } 尾遞歸 定義:函數尾調用自身 作用:只要使用尾遞歸就不會發生棧溢出,相對節省內存 實現:把所有用到的內部變量改寫成函數的參數並使用參數默認值
箭頭函數誤區
- 函數體內的this是定義時所在的對象而不是使用時所在的對象
- 可讓this指向固定化,這種特性很有利於封裝回調函數
- 不可當作構造函數,因此箭頭函數不可使用new命令
- 不可使用yield命令,因此箭頭函數不能用作Generator函數
- 不可使用Arguments對象,此對象在函數體內不存在(可用rest/spread參數代替)
- 返回對象時必須在對象外面加上括號
正則擴展
- 變更RegExp構造函數入參:允許首參數為正則對象,尾參數為正則修飾符(返回的正則表達式會忽略原正則表達式的修飾符)
- 正則方法調用變更:字符串對象的match()、replace()、search()、split()內部調用轉為調用RegExp實例對應的RegExp.prototype[Symbol.方法]
- u修飾符:Unicode模式修飾符,正確處理大於\\\\uFFFF的Unicode字符 點字符(.) Unicode表示法 量詞 預定義模式 i修飾符 轉義
- y修飾符:粘連修飾符,確保匹配必須從剩餘的第一個位置開始全局匹配(與g修飾符作用類似)
- unicode:是否設置u修飾符
- sticky:是否設置y修飾符
- flags:返回正則表達式的修飾符
重點難點
- y修飾符隱含頭部匹配標誌^
- 單單一個y修飾符對match()只能返回第一個匹配,必須與g修飾符聯用才能返回所有匹配
Symbol
- 定義:獨一無二的值
- 聲明:const set = Symbol(str)
- 入參:字符串(可選)
- 方法 Symbol():創建以參數作為描述的Symbol值(不登記在全局環境) Symbol.for():創建以參數作為描述的Symbol值,如存在此參數則返回原有的Symbol值(先搜索後創建,登記在全局環境) Symbol.keyFor():返回已登記的Symbol值的描述(只能返回Symbol.for()的key) Object.getOwnPropertySymbols():返回對象中所有用作屬性名的Symbol值的數組
- 內置 Symbol.hasInstance:指向一個內部方法,當其他對象使用instanceof運算符判斷是否為此對象的實例時會調用此方法 Symbol.isConcatSpreadable:指向一個布爾,定義對象用於Array.prototype.concat()時是否可展開 Symbol.species:指向一個構造函數,當實例對象使用自身構造函數時會調用指定的構造函數 Symbol.match:指向一個函數,當實例對象被String.prototype.match()調用時會重新定義match()的行為 Symbol.replace:指向一個函數,當實例對象被String.prototype.replace()調用時會重新定義replace()的行為 Symbol.search:指向一個函數,當實例對象被String.prototype.search()調用時會重新定義search()的行為 Symbol.split:指向一個函數,當實例對象被String.prototype.split()調用時會重新定義split()的行為 Symbol.iterator:指向一個默認遍歷器方法,當實例對象執行for-of時會調用指定的默認遍歷器 Symbol.toPrimitive:指向一個函數,當實例對象被轉為原始類型的值時會返回此對象對應的原始類型值 Symbol.toStringTag:指向一個函數,當實例對象被Object.prototype.toString()調用時其返回值會出現在toString()返回的字符串之中表示對象的類型 Symbol.unscopables:指向一個對象,指定使用with時哪些屬性會被with環境排除
數據類型
- Undefined
- Null
- String
- Number
- Boolean
- Object(包含Array、Function、Date、RegExp、Error)
- Symbol
應用場景
- 唯一化對象屬性名:屬性名屬於Symbol類型,就都是獨一無二的,可保證不會與其他屬性名產生衝突
- 消除魔術字符串:在代碼中多次出現且與代碼形成強耦合的某一個具體的字符串或數值
- 遍歷屬性名:無法通過for-in、for-of、Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回,只能通過Object.getOwnPropertySymbols返回
- 啟用模塊的Singleton模式:調用一個類在任何時候返回同一個實例(window和global),使用Symbol.for()來模擬全局的Singleton模式
重點難點
- Symbol()生成一個原始類型的值不是對象,因此Symbol()前不能使用new命令
- Symbol()參數表示對當前Symbol值的描述,相同參數的Symbol()返回值不相等
- Symbol值不能與其他類型的值進行運算
- Symbol值可通過String()或toString()顯式轉為字符串
- Symbol值作為對象屬性名時,此屬性是公開屬性,但不是私有屬性
- Symbol值作為對象屬性名時,只能用方括號運算符([])讀取,不能用點運算符(.)讀取
- Symbol值作為對象屬性名時,不會被常規方法遍歷得到,可利用此特性為對象定義非私有但又只用於內部的方法
Set
Set
- 定義:類似於數組的數據結構,成員值都是唯一且沒有重複的值
- 聲明:const set = new Set(arr)
- 入參:具有Iterator接口的數據結構
- 屬性 constructor:構造函數,返回Set size:返回實例成員總數
- 方法 add() :添加值,返回實例 delete():刪除值,返回布爾 has():檢查值,返回布爾 clear():清除所有成員 keys():返回以屬性值為遍歷器的對象 values():返回以屬性值為遍歷器的對象 entries():返回以屬性值和屬性值為遍歷器的對象 forEach():使用回調函數遍歷每個成員
應用場景
- 去重字符串:[...new Set(str)].join("")
- 去重數組:[...new Set(arr)]或Array.from(new Set(arr))
- 集合數組 聲明:const a = new Set(arr1)、const b = new Set(arr2) 並集:new Set([...a, ...b]) 交集:new Set([...a].filter(v => b.has(v))) 差集:new Set([...a].filter(v => !b.has(v)))
- 映射集合 聲明:let set = new Set(arr) 映射:set = new Set([...set].map(v => v * 2))或set = new Set(Array.from(set, v => v * 2))
重點難點
- 遍歷順序:插入順序
- 沒有鍵只有值,可認為鍵和值兩值相等
- 添加多個NaN時,只會存在一個NaN
- 添加相同的對象時,會認為是不同的對象
- 添加值時不會發生類型轉換(5 !== "5")
- keys()和values()的行為完全一致,entries()返回的遍歷器同時包括鍵和值且兩值相等
WeakSet
- 定義:和Set結構類似,成員值只能是對象
- 聲明:const set = new WeakSet(arr)
- 入參:具有Iterator接口的數據結構
- 屬性 constructor:構造函數,返回WeakSet
- 方法 add():添加值,返回實例 delete():刪除值,返回布爾 has():檢查值,返回布爾
應用場景
- 儲存DOM節點:DOM節點被移除時自動釋放此成員,不用擔心這些節點從文檔移除時會引發內存洩漏
- 臨時存放一組對象或存放跟對象綁定的信息:只要這些對象在外部消失,它在WeakSet結構中的引用就會自動消
重點難點
- 成員都是弱引用,垃圾回收機制不考慮WeakSet結構對此成員的引用
- 成員不適合引用,它會隨時消失,因此ES6規定WeakSet結構不可遍歷
- 其他對象不再引用成員時,垃圾回收機制會自動回收此成員所佔用的內存,不考慮此成員是否還存在於WeakSet結構中
Map
Map
- 定義:類似於對象的數據結構,成員鍵是任何類型的值
- 聲明:const set = new Map(arr)
- 入參:具有Iterator接口且每個成員都是一個雙元素數組的數據結構
- 屬性 constructor:構造函數,返回Map size :返回實例成員總數
- 方法 get():返回鍵值對 set():添加鍵值對,返回實例 delete():刪除鍵值對,返回布爾 has():檢查鍵值對,返回布爾 clear():清除所有成員 keys():返回以鍵為遍歷器的對象 values():返回以值為遍歷器的對象 entries():返回以鍵和值為遍歷器的對象 forEach():使用回調函數遍歷每個成員
重點難點
- 遍歷順序:插入順序
- 對同一個鍵多次賦值,後面的值將覆蓋前面的值
- 對同一個對象的引用,被視為一個鍵
- 對同樣值的兩個實例,被視為兩個鍵
- 鍵跟內存地址綁定,只要內存地址不一樣就視為兩個鍵
- 添加多個以NaN作為鍵時,只會存在一個以NaN作為鍵的值
- Object結構提供字符串—值的對應,Map結構提供值—值的對應
WeakMap
- 定義:和Map結構類似,成員鍵只能是對象
- 聲明:const set = new WeakMap(arr)
- 入參:具有Iterator接口且每個成員都是一個雙元素數組的數據結構
- 屬性 constructor:構造函數,返回WeakMap
- 方法 get():返回鍵值對 set():添加鍵值對,返回實例 delete():刪除鍵值對,返回布爾 has():檢查鍵值對,返回布爾
應用場景
- 儲存DOM節點:DOM節點被移除時自動釋放此成員鍵,不用擔心這些節點從文檔移除時會引發內存洩漏
- 部署私有屬性:內部屬性是實例的弱引用,刪除實例時它們也隨之消失,不會造成內存洩漏
重點難點
- 成員鍵都是弱引用,垃圾回收機制不考慮WeakMap結構對此成員鍵的引用
- 成員鍵不適合引用,它會隨時消失,因此ES6規定WeakMap結構不可遍歷
- 其他對象不再引用成員鍵時,垃圾回收機制會自動回收此成員所佔用的內存,不考慮此成員是否還存在於WeakMap結構中
- 一旦不再需要,成員會自動消失,不用手動刪除引用
- 弱引用的只是鍵而不是值,值依然是正常引用
- 即使在外部消除了成員鍵的引用,內部的成員值依然存在
Proxy
- 定義:修改某些操作的默認行為
- 聲明:const proxy = new Proxy(target, handler)
- 入參 target:攔截的目標對象 handler:定製攔截行為
- 方法 Proxy.revocable():返回可取消的Proxy實例(返回{ proxy, revoke },通過revoke()取消代理)
- 攔截方式 get():攔截對象屬性讀取 set():攔截對象屬性設置,返回布爾 has():攔截對象屬性檢查k in obj,返回布爾 deleteProperty():攔截對象屬性刪除delete obj[k],返回布爾 defineProperty():攔截對象屬性定義Object.defineProperty()、Object.defineProperties(),返回布爾 ownKeys():攔截對象屬性遍歷for-in、Object.keys()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols(),返回數組 getOwnPropertyDescriptor():攔截對象屬性描述讀取Object.getOwnPropertyDescriptor(),返回對象 getPrototypeOf():攔截對象原型讀取instanceof、Object.getPrototypeOf()、Object.prototype.__proto__、Object.prototype.isPrototypeOf()、Reflect.getPrototypeOf(),返回對象 setPrototypeOf():攔截對象原型設置Object.setPrototypeOf(),返回布爾 isExtensible():攔截對象是否可擴展讀取Object.isExtensible(),返回布爾 preventExtensions():攔截對象不可擴展設置Object.preventExtensions(),返回布爾 apply():攔截Proxy實例作為函數調用proxy()、proxy.apply()、proxy.call() construct():攔截Proxy實例作為構造函數調用new proxy()
應用場景
- Proxy.revocable():不允許直接訪問對象,必須通過代理訪問,一旦訪問結束就收回代理權不允許再次訪問
- get():讀取未知屬性報錯、讀取數組負數索引的值、封裝鏈式操作、生成DOM嵌套節點
- set():數據綁定(Vue數據綁定實現原理)、確保屬性值設置符合要求、防止內部屬性被外部讀寫
- has():隱藏內部屬性不被發現、排除不符合屬性條件的對象
- deleteProperty():保護內部屬性不被刪除
- defineProperty():阻止屬性被外部定義
- ownKeys():保護內部屬性不被遍歷
重點難點
- 要使Proxy起作用,必須針對實例進行操作,而不是針對目標對象進行操作
- 沒有設置任何攔截時,等同於直接通向原對象
- 屬性被定義為不可讀寫/擴展/配置/枚舉時,使用攔截方法會報錯
- 代理下的目標對象,內部this指向Proxy代理
Reflect
- 定義:保持Object方法的默認行為
- 方法 get():返回對象屬性 set():設置對象屬性,返回布爾 has():檢查對象屬性,返回布爾 deleteProperty():刪除對象屬性,返回布爾 defineProperty():定義對象屬性,返回布爾 ownKeys():遍歷對象屬性,返回數組(Object.getOwnPropertyNames()+Object.getOwnPropertySymbols()) getOwnPropertyDescriptor():返回對象屬性描述,返回對象 getPrototypeOf():返回對象原型,返回對象 setPrototypeOf():設置對象原型,返回布爾 isExtensible():返回對象是否可擴展,返回布爾 preventExtensions():設置對象不可擴展,返回布爾 apply():綁定this後執行指定函數 construct():調用構造函數創建實例
設計目的
- 將Object屬於語言內部的方法放到Reflect上
- 將某些Object方法報錯情況改成返回false
- 讓Object操作變成函數行為
- Proxy與Reflect相輔相成
廢棄方法
- Object.defineProperty() => Reflect.defineProperty()
- Object.getOwnPropertyDescriptor() => Reflect.getOwnPropertyDescriptor()
重點難點
- Proxy方法和Reflect方法一一對應
- Proxy和Reflect聯合使用,前者負責攔截賦值操作,後者負責完成賦值操作
數據綁定:觀察者模式
<code>const observerQueue = new Set();
const observe = fn => observerQueue.add(fn);
const observable = obj => new Proxy(obj, {
set(tgt, key, val, receiver) {
const result = Reflect.set(tgt, key, val, receiver);
observerQueue.forEach(v => v());
return result;
}
});
const person = observable({ age: 25, name: "Yajun" });
const print = () => console.log(`${person.name} is ${person.age} years old`);
observe(print);
person.name = "Joway";
複製代碼/<code>
Class
- 定義:對一類具有共同特徵的事物的抽象(構造函數語法糖)
- 原理:類本身指向構造函數,所有方法定義在prototype上,可看作構造函數的另一種寫法(Class === Class.prototype.constructor)
- 方法和關鍵字 constructor():構造函數,new命令生成實例時自動調用 extends:繼承父類 super:新建父類的this static :定義靜態屬性方法 get:取值函數,攔截屬性的取值行為 set:存值函數,攔截屬性的存值行為
- 屬性 __proto__:構造函數的繼承(總是指向父類) __proto__.__proto__:子類的原型的原型,即父類的原型(總是指向父類的__proto__) prototype.__proto__:屬性方法的繼承(總是指向父類的prototype)
- 靜態屬性:定義類完成後賦值屬性,該屬性不會被實例繼承,只能通過類來調用
- 靜態方法:使用static定義方法,該方法不會被實例繼承,只能通過類來調用(方法中的this指向類,而不是實例)
- 繼承 實質 ES5實質:先創造子類實例的this,再將父類的屬性方法添加到this上(Parent.apply(this)) ES6實質:先將父類實例的屬性方法加到this上(調用super()),再用子類構造函數修改this super 作為函數調用:只能在構造函數中調用super(),內部this指向繼承的當前子類(super()調用後才可在構造函數中使用this) 作為對象調用:在普通方法中指向父類的原型對象,在靜態方法中指向父類 顯示定義:使用constructor() { super(); }定義繼承父類,沒有書寫則顯示定義 子類繼承父類:子類使用父類的屬性方法時,必須在構造函數中調用super(),否則得不到父類的this 父類靜態屬性方法可被子類繼承 子類繼承父類後,可從super上調用父類靜態屬性方法
- 實例:類相當於實例的原型,所有在類中定義的屬性方法都會被實例繼承 顯式指定屬性方法:使用this指定到自身上(使用Class.hasOwnProperty()可檢測到) 隱式指定屬性方法:直接聲明定義在對象原型上(使用Class.__proto__.hasOwnProperty()可檢測到)
- 表達式 類表達式:const Class = class {} name屬性:返回緊跟class後的類名 屬性表達式:[prop] Generator方法:* mothod() {} Async方法:async mothod() {}
- this指向:解構實例屬性或方法時會報錯 綁定this:this.mothod = this.mothod.bind(this) 箭頭函數:this.mothod = () => this.mothod()
- 屬性定義位置 定義在構造函數中並使用this指向 定義在類最頂層
- new.target:確定構造函數是如何調用
原生構造函數
- String()
- Number()
- Boolean()
- Array()
- Object()
- Function()
- Date()
- RegExp()
- Error()
重點難點
- 在實例上調用方法,實質是調用原型上的方法
- Object.assign()可方便地一次向類添加多個方法(Object.assign(Class.prototype, { ... }))
- 類內部所有定義的方法是不可枚舉的(non-enumerable)
- 構造函數默認返回實例對象(this),可指定返回另一個對象
- 取值函數和存值函數設置在屬性的Descriptor對象上
- 類不存在變量提升
- 利用new.target === Class寫出不能獨立使用必須繼承後才能使用的類
- 子類繼承父類後,this指向子類實例,通過super對某個屬性賦值,賦值的屬性會變成子類實例的屬性
- 使用super時,必須顯式指定是作為函數還是作為對象使用
- extends不僅可繼承類還可繼承原生的構造函數
私有屬性方法
<code>const name = Symbol("name");
const print = Symbol("print");
class Person {
constructor(age) {
this[name] = "Bruce";
this.age = age;
}
[print]() {
console.log(`${this[name]} is ${this.age} years old`);
}
}
複製代碼/<code>
繼承混合類
<code>function CopyProperties(target, source) {
for (const key of Reflect.ownKeys(source)) {
if (key !== "constructor" && key !== "prototype" && key !== "name") {
const desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
}
function MixClass(...mixins) {
class Mix {
constructor() {
for (const mixin of mixins) {
CopyProperties(this, new mixin());
}
}
}
for (const mixin of mixins) {
CopyProperties(Mix, mixin);
CopyProperties(Mix.prototype, mixin.prototype);
}
return Mix;
}
class Student extends MixClass(Person, Kid) {}
複製代碼/<code>
Module
- 命令 export:規定模塊對外接口 默認導出:export default Person(導入時可指定模塊任意名稱,無需知曉內部真實名稱) 單獨導出:export const name = "Bruce" 按需導出:export { age, name, sex }(推薦) 改名導出:export { name as newName } import:導入模塊內部功能 默認導入:import Person from "person" 整體導入:import * as Person from "person" 按需導入:import { age, name, sex } from "person" 改名導入:import { name as newName } from "person" 自執導入:import "person" 複合導入:import Person, { name } from "person" 複合模式:export命令和import命令結合在一起寫成一行,變量實質沒有被導入當前模塊,相當於對外轉發接口,導致當前模塊無法直接使用其導入變量 默認導入導出:export { default } from "person" 整體導入導出:export * from "person" 按需導入導出:export { age, name, sex } from "person" 改名導入導出:export { name as newName } from "person" 具名改默認導入導出:export { name as default } from "person" 默認改具名導入導出:export { default as name } from "person"
- 繼承:默認導出和改名導出結合使用可使模塊具備繼承性
- 設計思想:儘量地靜態化,使得編譯時就能確定模塊的依賴關係,以及輸入和輸出的變量
- 嚴格模式:ES6模塊自動採用嚴格模式(不管模塊頭部是否添加use strict)
模塊方案
- CommonJS:用於服務器(動態化依賴)
- AMD:用於瀏覽器(動態化依賴)
- CMD:用於瀏覽器(動態化依賴)
- UMD:用於瀏覽器和服務器(動態化依賴)
- ESM:用於瀏覽器和服務器(靜態化依賴)
加載方式
- 運行時加載 定義:整體加載模塊生成一個對象,再從對象上獲取需要的屬性和方法進行加載(全部加載) 影響:只有運行時才能得到這個對象,導致無法在編譯時做靜態優化
- 編譯時加載 定義:直接從模塊中獲取需要的屬性和方法進行加載(按需加載) 影響:在編譯時就完成模塊加載,效率比其他方案高,但無法引用模塊本身(本身不是對象),可拓展JS高級語法(宏和類型校驗)
加載實現
- 傳統加載 :通過 Defer異步加載:(順序加載,渲染完再執行) Async異步加載:(亂序加載,下載完就執行)
- 模塊加載:(默認是Defer異步加載)
CommonJS和ESM的區別
- CommonJS輸出值的拷貝,ESM輸出值的引用 CommonJS一旦輸出一個值,模塊內部的變化就影響不到這個值 ESM是動態引用且不會緩存值,模塊裡的變量綁定其所在的模塊,等到腳本真正執行時,再根據這個只讀引用到被加載的那個模塊裡去取值
- CommonJS是運行時加載,ESM是編譯時加載 CommonJS加載模塊是對象(即module.exports),該對象只有在腳本運行完才會生成 ESM加載模塊不是對象,它的對外接口只是一種靜態定義,在代碼靜態解析階段就會生成
Node加載
- 背景:CommonJS和ESM互不兼容,目前解決方案是將兩者分開,採用各自的加載方案
- 區分:要求ESM採用.mjs後綴文件名 require()不能加載.mjs文件,只有import命令才可加載.mjs文件 .mjs文件裡不能使用require(),必須使用import命令加載文件
- 驅動:node --experimental-modules file.mjs
- 限制:Node的import命令目前只支持加載本地模塊(file:協議),不支持加載遠程模塊
- 加載優先級 腳本文件省略後綴名:依次嘗試加載四個後綴名文件(.mjs、.js、.json、node) 以上不存在:嘗試加載package.json的main字段指定的腳本 以上不存在:依次嘗試加載名稱為index四個後綴名文件(.mjs、.js、.json、node) 以上不存在:報錯
- 不存在的內部變量:arguments、exports、module、require、this、__dirname、__filename
- CommonJS加載ESM 不能使用require(),只能使用import()
- ESM加載CommonJS 自動將module.exports轉化成export default CommonJS輸出緩存機制在ESM加載方式下依然有效 採用import命令加載CommonJS模塊時,不允許採用按需導入,應使用默認導入或整體導入
循環加載
- 定義:腳本A的執行依賴腳本B,而腳本A的執行又依賴腳本B
- 加載原理 CommonJS:require()首次加載腳本就會執行整個腳本,在內存裡生成一個對象緩存下來,二次加載腳本時直接從緩存中獲取 ESM:import命令加載變量不會被緩存,而是成為一個指向被加載模塊的引用
- 循環加載 CommonJS:只輸出已經執行的部分,還未執行的部分不會輸出 ESM:需開發者自己保證真正取值時能夠取到值(可把變量寫成函數形式,函數具有提升作用)
重點難點
- ES6模塊中,頂層this指向undefined,不應該在頂層代碼使用this
- 一個模塊就是一個獨立的文件,該文件內部的所有變量,外部無法獲取
- export命令輸出的接口與其對應的值是動態綁定關係,即通過該接口可獲取模塊內部實時的值
- import命令大括號裡的變量名必須與被導入模塊對外接口的名稱相同
- import命令輸入的變量只讀(本質是輸入接口),不允許在加載模塊的腳本里改寫接口
- import命令命令具有提升效果,會提升到整個模塊的頭部,首先執行
- 重複執行同一句import語句,只會執行一次
- export default命令只能使用一次
- export default命令導出的整體模塊,在執行import命令時其後不能跟大括號
- export default命令本質是輸出一個名為default的變量,後面不能跟變量聲明語句
- export default命令本質是將後面的值賦給名為default的變量,可直接將值寫在其後
- export default命令和export {}命令可同時存在,對應複合導入
- export命令和import命令可出現在模塊任何位置,只要處於模塊頂層即可,不能處於塊級作用域
- import()加載模塊成功後,此模塊會作為一個對象,當作then()的參數,可使用對象解構賦值來獲取輸出接口
- 同時動態加載多個模塊時,可使用Promise.all()和import()相結合來實現
- import()和結合async/await來書寫同步操作的代碼
單例模式:跨模塊常量
<code>// 常量跨文件共享
// person.js
const NAME = "Bruce";
const AGE = 25;
const SEX = "male";
export { AGE, NAME, SEX };
複製代碼/<code>
<code>// file1.js
import { AGE } from "person";
console.log(AGE);
複製代碼/<code>
<code>// file2.js
import { AGE, NAME, SEX } from "person";
console.log(AGE, NAME, SEX);
複製代碼/<code>
默認導入互換整體導入
<code>import Person from "person";
console.log(Person.AGE);
複製代碼/<code>
<code>import * as Person from "person";
console.log(Person.default.AGE);
複製代碼/<code>
Iterator
- 定義:為各種不同的數據結構提供統一的訪問機制
- 原理:創建一個指針指向首個成員,按照次序使用next()指向下一個成員,直接到結束位置(數據結構只要部署Iterator接口就可完成遍歷操作)
- 作用 為各種數據結構提供一個統一的簡便的訪問接口 使得數據結構成員能夠按某種次序排列 ES6創造了新的遍歷命令for-of,Iterator接口主要供for-of消費
- 形式:for-of(自動去尋找Iterator接口)
- 數據結構 集合:Array、Object、Set、Map 原生具備接口的數據結構:String、Array、Set、Map、TypedArray、Arguments、NodeList
- 部署:默認部署在Symbol.iterator(具備此屬性被認為可遍歷的iterable)
- 遍歷器對象 next():下一步操作,返回{ done, value }(必須部署) return():for-of提前退出調用,返回{ done: true } throw():不使用,配合Generator函數使用
ForOf循環
- 定義:調用Iterator接口產生遍歷器對象(for-of內部調用數據結構的Symbol.iterator())
- 遍歷字符串:for-in獲取索引,for-of獲取值(可識別32位UTF-16字符)
- 遍歷數組:for-in獲取索引,for-of獲取值
- 遍歷對象:for-in獲取鍵,for-of需自行部署
- 遍歷Set:for-of獲取值 => for (const v of set)
- 遍歷Map:for-of獲取鍵值對 => for (const [k, v] of map)
- 遍歷類數組:包含length的對象、Arguments對象、NodeList對象(無Iterator接口的類數組可用Array.from()轉換)
- 計算生成數據結構:Array、Set、Map keys():返回遍歷器對象,遍歷所有的鍵 values():返回遍歷器對象,遍歷所有的值 entries():返回遍歷器對象,遍歷所有的鍵值對
- 與for-in區別 有著同for-in一樣的簡潔語法,但沒有for-in那些缺點、 不同於forEach(),它可與break、continue和return配合使用 提供遍歷所有數據結構的統一操作接口
應用場景
- 改寫具有Iterator接口的數據結構的Symbol.iterator
- 解構賦值:對Set進行結構
- 擴展運算符:將部署Iterator接口的數據結構轉為數組
- yield*:yield*後跟一個可遍歷的數據結構,會調用其遍歷器接口
- 接受數組作為參數的函數:for-of、Array.from()、new Set()、new WeakSet()、new Map()、new WeakMap()、Promise.all()、Promise.race()
Promise
- 定義:包含異步操作結果的對象
- 狀態 進行中:pending 已成功:resolved 已失敗:rejected
- 特點 對象的狀態不受外界影響 一旦狀態改變就不會再變,任何時候都可得到這個結果
- 聲明:new Promise((resolve, reject) => {})
- 出參 resolve:將狀態從未完成變為成功,在異步操作成功時調用,並將異步操作的結果作為參數傳遞出去 reject:將狀態從未完成變為失敗,在異步操作失敗時調用,並將異步操作的錯誤作為參數傳遞出去
- 方法 then():分別指定resolved狀態和rejected狀態的回調函數 第一參數:狀態變為resolved時調用 第二參數:狀態變為rejected時調用(可選) catch():指定發生錯誤時的回調函數 Promise.all():將多個實例包裝成一個新實例,返回全部實例狀態變更後的結果數組(齊變更再返回) 入參:具有Iterator接口的數據結構 成功:只有全部實例狀態變成fulfilled,最終狀態才會變成fulfilled 失敗:其中一個實例狀態變成rejected,最終狀態就會變成rejected Promise.race() :將多個實例包裝成一個新實例,返回全部實例狀態優先變更後的結果(先變更先返回) 入參:具有Iterator接口的數據結構 成功失敗:哪個實例率先改變狀態就返回哪個實例的狀態 Promise.resolve():將對象轉為Promise對象(等價於new Promise(resolve => resolve())) Promise實例:原封不動地返回入參 Thenable對象:將此對象轉為Promise對象並返回(Thenable為包含then()的對象,執行then()相當於執行此對象的then()) 不具有then()的對象:將此對象轉為Promise對象並返回,狀態為resolved 不帶參數:返回Promise對象,狀態為resolved Promise.reject():將對象轉為狀態為rejected的Promise對象(等價於new Promise((resolve, reject) => reject()))
應用場景
- 加載圖片
- AJAX轉Promise對象
重點難點
- 只有異步操作的結果可決定當前狀態是哪一種,其他操作都無法改變這個狀態
- 狀態改變只有兩種可能:從pending變為resolved、從pending變為rejected
- 一旦新建Promise對象就會立即執行,無法中途取消
- 不設置回調函數,內部拋錯不會反應到外部
- 當處於pending時,無法得知目前進展到哪一個階段
- 實例狀態變為resolved或rejected時,會觸發then()綁定的回調函數
- resolve()和reject()的執行總是晚於本輪循環的同步任務
- then()返回新實例,其後可再調用另一個then()
- then()運行中拋出錯誤會被catch()捕獲
- reject()的作用等同於拋出錯誤
- 實例狀態已變成resolved時,再拋出錯誤是無效的,不會被捕獲,等於沒有拋出
- 實例狀態的錯誤具有冒泡性質,會一直向後傳遞直到被捕獲為止,錯誤總是會被下一個catch()捕獲
- 不要在then()裡定義rejected狀態的回調函數(不使用其第二參數)
- 建議使用catch()捕獲錯誤,不要使用then()第二個參數捕獲
- 沒有使用catch()捕獲錯誤,實例拋錯不會傳遞到外層代碼,即不會有任何反應
- 作為參數的實例定義了catch(),一旦被rejected並不會觸發Promise.all()的catch()
- Promise.reject()的參數會原封不動地作為rejected的理由,變成後續方法的參數
Generator
- 定義:封裝多個內部狀態的異步編程解決方案
- 形式:調用Generator函數(該函數不執行)返回指向內部狀態的指針對象(不是運行結果)
- 聲明:function* Func() {}
- 方法 next():使指針移向下一個狀態,返回{ done, value }(入參會被當作上一個yield命令表達式的返回值) return():返回指定值且終結遍歷Generator函數,返回{ done: true, value: 入參 } throw():在Generator函數體外拋出錯誤,在Generator函數體內捕獲錯誤,返回自定義的new Errow()
- yield命令:聲明內部狀態的值(return聲明結束返回的值) 遇到yield命令就暫停執行後面的操作,並將其後表達式的值作為返回對象的value 下次調用next()時,再繼續往下執行直到遇到下一個yield命令 沒有再遇到yield命令就一直運行到Generator函數結束,直到遇到return語句為止並將其後表達式的值作為返回對象的value Generator函數沒有return語句則返回對象的value為undefined
- yield*命令:在一個Generator函數里執行另一個Generator函數(後隨具有Iterator接口的數據結構)
- 遍歷:通過for-of自動調用next()
- 作為對象屬性 全寫:const obj = { method: function*() {} } 簡寫:const obj = { * method() {} }
- 上下文:執行產生的上下文環境一旦遇到yield命令就會暫時退出堆棧(但並不消失),所有變量和對象會凍結在當前狀態,等到對它執行next()時,這個上下文環境又會重新加入調用棧,凍結的變量和對象恢復執行
方法異同
- 相同點:next()、throw()、return()本質上是同一件事,作用都是讓函數恢復執行且使用不同的語句替換yield命令
- 不同點 next():將yield命令替換成一個值 return():將yield命令替換成一個return語句 throw():將yield命令替換成一個throw語句
應用場景
- 異步操作同步化表達
- 控制流管理
- 為對象部署Iterator接口:把Generator函數賦值給對象的Symbol.iterator,從而使該對象具有Iterator接口
- 作為具有Iterator接口的數據結構
重點難點
- 每次調用next(),指針就從函數頭部或上次停下的位置開始執行,直到遇到下一個yield命令或return語句為止
- 函數內部可不用yield命令,但會變成單純的暫緩執行函數(還是需要next()觸發)
- yield命令是暫停執行的標記,next()是恢復執行的操作
- yield命令用在另一個表達式中必須放在圓括號裡
- yield命令用作函數參數或放在賦值表達式的右邊,可不加圓括號
- yield命令本身沒有返回值,可認為是返回undefined
- yield命令表達式為惰性求值,等next()執行到此才求值
- 函數調用後生成遍歷器對象,此對象的Symbol.iterator是此對象本身
- 在函數運行的不同階段,通過next()從外部向內部注入不同的值,從而調整函數行為
- 首個next()用來啟動遍歷器對象,後續才可傳遞參數
- 想首次調用next()時就能輸入值,可在函數外面再包一層
- 一旦next()返回對象的done為true,for-of遍歷會中止且不包含該返回對象
- 函數內部部署try-finally且正在執行try,那麼return()會導致立刻進入finally,執行完finally以後整個函數才會結束
- 函數內部沒有部署try-catch,throw()拋錯將被外部try-catch捕獲
- throw()拋錯要被內部捕獲,前提是必須至少執行過一次next()
- throw()被捕獲以後,會附帶執行下一條yield命令
- 函數還未開始執行,這時throw()拋錯只可能拋出在函數外部
首次next()可傳值
<code>function Wrapper(func) {
return function(...args) {
const generator = func(...args);
generator.next();
return generator;
}
}
const print = Wrapper(function*() {
console.log(`First Input: ${yield}`);
return "done";
});
print().next("hello");
複製代碼/<code>
ES2016
數值擴展
- 指數運算符(**) :數值求冪(相當於Math.pow())
數組擴展
- includes():是否存在指定成員
ES2017
- 共享內存和原子操作:由全局對象SharedArrayBuffer和Atomics實現,將數據存儲在一塊共享內存空間中,這些數據可在JS主線程和web-worker線程之間共享
字符串擴展
- padStart():把指定字符串填充到字符串頭部,返回新字符串
- padEnd():把指定字符串填充到字符串尾部,返回新字符串
對象擴展
- Object.getOwnPropertyDescriptors():返回對象所有自身屬性(非繼承屬性)的描述對象
- Object.values():返回以值組成的數組
- Object.entries():返回以鍵和值組成的數組
函數擴展
- 函數參數尾逗號:允許函數最後一個參數有尾逗號
Async
- 定義:使異步函數以同步函數的形式書寫(Generator函數語法糖)
- 原理:將Generator函數和自動執行器spawn包裝在一個函數里
- 形式:將Generator函數的*替換成async,將yield替換成await
- 聲明 具名函數:async function Func() {} 函數表達式:const func = async function() {} 箭頭函數:const func = async() => {} 對象方法:const obj = { async func() {} } 類方法:class Cla { async Func() {} }
- await命令:等待當前Promise對象狀態變更完畢 正常情況:後面是Promise對象則返回其結果,否則返回對應的值 後隨Thenable對象:將其等同於Promise對象返回其結果
- 錯誤處理:將await命令Promise對象放到try-catch中(可放多個)
Async對Generator改進
- 內置執行器
- 更好的語義
- 更廣的適用性
- 返回值是Promise對象
應用場景
- 按順序完成異步操作
重點難點
- Async函數返回Promise對象,可使用then()添加回調函數
- 內部return返回值會成為後續then()的出參
- 內部拋出錯誤會導致返回的Promise對象變為rejected狀態,被catch()接收到
- 返回的Promise對象必須等到內部所有await命令Promise對象執行完才會發生狀態改變,除非遇到return語句或拋出錯誤
- 任何一個await命令Promise對象變為rejected狀態,整個Async函數都會中斷執行
- 希望即使前一個異步操作失敗也不要中斷後面的異步操作 將await命令Promise對象放到try-catch中 await命令Promise對象跟一個catch()
- await命令Promise對象可能變為rejected狀態,最好把其放到try-catch中
- 多個await命令Promise對象若不存在繼發關係,最好讓它們同時觸發
- await命令只能用在Async函數之中,否則會報錯
- 數組使用forEach()執行async/await會失效,可使用for-of和Promise.all()代替
- 可保留運行堆棧,函數上下文隨著Async函數的執行而存在,執行完成就消失
ES2018
字符串擴展
- 放鬆對標籤模板裡字符串轉義的限制:遇到不合法的字符串轉義返回undefined,並且從raw上可獲取原字符串
對象擴展
- 擴展運算符(...):轉換對象為用逗號分隔的參數序列({ ...obj },相當於rest/spread參數的逆運算)
擴展應用
- 克隆對象:const obj = { __proto__: Object.getPrototypeOf(obj1), ...obj1 }
- 合併對象:const obj = { ...obj1, ...obj2 }
- 轉換字符串為對象:{ ..."hello" }
- 轉換數組為對象:{ ...[1, 2] }
- 與對象解構賦值結合:const { x, ...rest/spread } = { x: 1, y: 2, z: 3 }(不能複製繼承自原型對象的屬性)
- 修改現有對象部分屬性:const obj = { x: 1, ...{ x: 2 } }
正則擴展
- s修飾符:dotAll模式修飾符,使.匹配任意單個字符(dotAll模式)
- dotAll:是否設置s修飾符
- 後行斷言:x只有在y後才匹配
- 後行否定斷言:x只有不在y後才匹配
- Unicode屬性轉義:匹配符合Unicode某種屬性的所有字符 正向匹配:\\p{PropRule} 反向匹配:\\P{PropRule} 限制:\\p{...}和\\P{...}只對Unicode字符有效,使用時需加上u修飾符
- 具名組匹配:為每組匹配指定名字(?<groupname>) 形式:str.exec().groups.GroupName 解構賦值替換 聲明:const time = "2017-09-11"、const regexp = /(?<year>\\d{4})-(?<month>\\d{2})-(?
\\d{2})/u 匹配:time.replace(regexp, "$ /<month>/<year>/<groupname>/$<month>/$<year>")/<year>/<month>
Promise
- finally():指定不管最後狀態如何都會執行的回調函數
Async
- 異步迭代器(for-await-of):循環等待每個Promise對象變為resolved狀態才進入下一步
ES2019
字符串擴展
- 直接輸入U+2028和U+2029:字符串可直接輸入行分隔符和段分隔符
- JSON.stringify()改造:可返回不符合UTF-8標準的字符串
- trimStart():消除字符串頭部空格,返回新字符串
- trimEnd():消除字符串尾部空格,返回新字符串
對象擴展
- Object.fromEntries():返回以鍵和值組成的對象(Object.entries()的逆操作)
數組擴展
- sort()穩定性:排序關鍵字相同的項目其排序前後的順序不變,默認為穩定
- flat() :扁平化數組,返回新數組
- flatMap():映射且扁平化數組,返回新數組(只能展開一層數組)
函數擴展
- toString()改造:返回函數原始代碼(與編碼一致)
- catch()參數可省略:catch()中的參數可省略
Symbol
- description:返回Symbol值的描述
ES2020
- globalThis:作為頂層對象,指向全局環境下的this Browser:頂層對象是window Node:頂層對象是global WebWorker:頂層對象是self 以上三者:通用頂層對象是globalThis
數值擴展
- BigInt:任何位數的整數(新增的數據類型,使用n結尾) BigInt():轉換普通數值為BigInt類型 BigInt.asUintN():轉換BigInt為0到2n-1之間對應的值 BigInt.asIntN():轉換BigInt為-2n-1 到2n-1-1 BigInt.parseInt():近似於Number.parseInt(),將一個字符串轉換成指定進制的BigInt類型
重點難點
- BigInt同樣可使用各種進製表示,都要加上後綴
- BigInt與普通整數是兩種值,它們之間並不相等
- typeof運算符對於BigInt類型的數據返回bigint
對象擴展
- 鏈判斷操作符(?.):是否存在對象屬性(不存在返回undefined且不再往下執行) 對象屬性:obj?.prop、obj?.[expr] 函數調用:func?.(...args)
- 空判斷操作符(??):是否值為undefined或null,是則使用默認值
正則擴展
- matchAll():返回所有匹配的遍歷器
Module
- import():動態導入(返回Promise) 背景:import命令被JS引擎靜態分析,先於模塊內的其他語句執行,無法取代require()的動態加載功能,提案建議引入import()來代替require() 位置:可在任何地方使用 區別:require()是同步加載,import()是異步加載 場景:按需加載、條件加載、模塊路徑動態化
Iterator
- for-in遍歷順序:不同的引擎已就如何迭代屬性達成一致,從而使行為標準化
Promise
- Promise.allSettled():將多個實例包裝成一個新實例,返回全部實例狀態變更後的狀態數組(齊變更再返回) 入參:具有Iterator接口的數據結構 成功:成員包含status和value,status為fulfilled,value為返回值 失敗:成員包含status和reason,status為rejected,value為錯誤原因
ES提案
- do表達式:封裝塊級作用域的操作,返回內部最後執行表達式的值(do{})
- throw表達式:直接使用throw new Error(),無需()或{}包括
- !#命令:指定腳本執行器(寫在文件首行)
數值擴展
- 數值分隔符(_):使用_作為千分位分隔符(增加數值的可讀性)
- Math.signbit():返回數值符號是否設置
函數擴展
- 函數部分執行:複用函數功能(?表示單個參數佔位符,...表示多個參數佔位符)
- 管道操作符(|>):把左邊表達式的值傳入右邊的函數進行求值(f(x) => x |> f)
- 綁定運算符(::):函數綁定(左邊是對象右邊是函數,取代bind、apply、call調用) bind:bar.bind(foo) => foo::bar apply:bar.apply(foo, arguments) => foo::bar(...arguments)
Realm
- 定義:提供沙箱功能,允許隔離代碼,防止被隔離的代碼拿到全局對象
- 聲明:new Realm().global
Class
- 靜態屬性:使用static定義屬性,該屬性不會被實例繼承,只能通過類來調用
- 私有屬性:使用#定義屬性,該屬性只能在類內部訪問
- 私有方法:使用#定義方法,該方法只能在類內部訪問
- 裝飾器:使用@註釋或修改類和類方法
Module
- import.meta:返回腳本元信息
Promise
- Promise.any():將多個實例包裝成一個新實例,返回全部實例狀態變更後的結果數組(齊變更再返回) 入參:具有Iterator接口的數據結構 成功:其中一個實例狀態變成fulfilled,最終狀態就會變成fulfilled 失敗:只有全部實例狀態變成rejected,最終狀態才會變成rejected
- Promise.try():不想區分是否同步異步函數,包裝函數為實例,使用then()指定下一步流程,使用catch()捕獲錯誤
Async
- 頂層Await:允許在模塊的頂層獨立使用await命令(借用await解決模塊異步加載的問題)
總結
最後送大家一張完整的ES6特性圖,記得給我點個贊喔,算是對我的一種鼓勵。因為圖片實在太大無法上傳,請關注IQ前端或掃描文章底部二維碼,後臺回覆ES6,獲取高清的ES6全部特性記憶圖,助你輕鬆記住ES6全部特性。
結語
閱讀更多 Echa攻城獅 的文章
關鍵字: 數據結構 JSON ECMAScript