關注Uzero
公眾號,更多前端小乾貨等著你喔!
前言
第三次閱讀阮一峰老師的《ES6標準入門》了,以前閱讀時不細心,很多地方都是一目十行。最近這次閱讀都是逐個逐個字來讀,發現很多以前都沒有注意到的知識點,為了方便記憶和預覽全部ES6特性
,所以寫下本文。
以下提到的《ES6標準入門》統一使用《ES6》這個名稱來代替,而最新的ES6版本也是截止到當前的ES2019
本文的知識點完全是參考或摘錄《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
。
所以有些文章上提到的ES7
(實質上是ES2016
)、ES8
(實質上是ES2017
)、ES9
(實質上是ES2018
)、ES10
(實質上是ES2019
),實質上都是一些不規範的概念。從ES1到ES6,每個標準都是花了好幾年甚至十多年才制定下來,你一個ES6到ES7,ES7到ES8,才用了一年,按照這樣的定義下去,那不是很快就ES20了。用正確的概念來說ES6目前涵蓋了ES2015、ES2016、ES2017、ES2018、ES2019。
另外,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命令
不存在變數提升 - 暫時性死區:在程式碼塊內使用
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
- 取值函式(getter)和存值函式(setter):
-
屬性的可列舉性和遍歷:描述物件的
enumerable
-
super關鍵字:指向當前物件的原型物件(只能用在物件的簡寫方法中
method() {}
) - Object.is():對比兩值是否相等
- Object.assign():合併物件(淺拷貝),返回原物件
- Object.getPrototypeOf():返回物件的原型物件
- Object.setPrototypeOf():設定物件的原型物件
- __proto__:返回或設定物件的原型物件
屬性遍歷
- 描述:
自身
、可繼承
、可列舉
、非列舉
、Symbol
-
遍歷
-
for-in
:遍歷物件自身可繼承可列舉
屬性 -
Object.keys()
:返回物件自身可列舉
屬性的鍵組成的陣列 -
Object.getOwnPropertyNames()
:返回物件自身可繼承可列舉非列舉
屬性的鍵組成的陣列 -
Object.getOwnPropertySymbols()
:返回物件Symbol
屬性的鍵組成的陣列 -
Reflect.ownKeys()
:返回物件自身可繼承可列舉非列舉Symbol
屬性的鍵組成的陣列
-
-
規則
- 首先遍歷所有數值鍵,按照數值升序排列
- 其次遍歷所有字串鍵,按照加入時間升序排列
- 最後遍歷所有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():建立以引數作為描述的
-
內建
-
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環境
排除
-
Symbol.hasInstance:指向一個內部方法,當其他物件使用
資料型別
- 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()取消代理)
-
Proxy.revocable():返回可取消的Proxy例項(返回
-
攔截方式
- 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
聯合使用,前者負責攔截賦值操作
,後者負責完成賦值操作
資料繫結:觀察者模式
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";
Class
- 定義:對一類具有共同特徵的事物的抽象(建構函式語法糖)
- 原理:類本身指向建構函式,所有方法定義在
prototype
上,可看作建構函式的另一種寫法(Class === Class.prototype.constructor
) -
方法和關鍵字
-
constructor():建構函式,
new命令
生成例項時自動呼叫 - extends:繼承父類
-
super:新建父類的
this
- static:定義靜態屬性方法
- get:取值函式,攔截屬性的取值行為
- set:存值函式,攔截屬性的存值行為
-
constructor():建構函式,
-
屬性
-
__proto__:
建構函式的繼承
(總是指向父類
) -
__proto__.__proto__:子類的原型的原型,即父類的原型(總是指向父類的
__proto__
) -
prototype.__proto__:
屬性方法的繼承
(總是指向父類的prototype
)
-
__proto__:
- 靜態屬性:定義類完成後賦值屬性,該屬性
不會被例項繼承
,只能通過類來呼叫 - 靜態方法:使用
static
定義方法,該方法不會被例項繼承
,只能通過類來呼叫(方法中的this
指向類,而不是例項) -
繼承
-
實質
- ES5實質:先創造子類例項的
this
,再將父類的屬性方法新增到this
上(Parent.apply(this)
) - ES6實質:先將父類例項的屬性方法加到
this
上(呼叫super()
),再用子類建構函式修改this
- ES5實質:先創造子類例項的
-
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:
-
屬性定義位置
- 定義在建構函式中並使用
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
不僅可繼承類還可繼承原生的建構函式
私有屬性方法
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`);
}
}
繼承混合類
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) {}
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高階語法(巨集和型別校驗)
載入實現
-
傳統載入:通過
<script>
進行同步或非同步載入指令碼- 同步載入:
<script src=""></script>
- Defer非同步載入:
<script src="" defer></script>
(順序載入,渲染完再執行) - Async非同步載入:
<script src="" async></script>
(亂序載入,下載完就執行)
- 同步載入:
-
模組載入:
<script type="module" src=""></script>
(預設是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:
-
迴圈載入
- 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
來書寫同步操作的程式碼
單例模式:跨模組常量
// 常量跨檔案共享
// person.js
const NAME = "Bruce";
const AGE = 25;
const SEX = "male";
export { AGE, NAME, SEX };
// file1.js
import { AGE } from "person";
console.log(AGE);
// file2.js
import { AGE, NAME, SEX } from "person";
console.log(AGE, NAME, SEX);
預設匯入互換整體匯入
import Person from "person";
console.log(Person.AGE);
import * as Person from "person";
console.log(Person.default.AGE);
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函式
使用
-
next():下一步操作,返回
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:將狀態從
未完成
變為失敗
,在非同步操作失敗時呼叫,並將非同步操作的錯誤作為引數傳遞出去
-
resolve:將狀態從
-
方法
-
then():分別指定
resolved狀態
和rejected狀態
的回撥函式-
第一引數:狀態變為
resolved
時呼叫 -
第二引數:狀態變為
rejected
時呼叫(可選)
-
第一引數:狀態變為
- catch():指定發生錯誤時的回撥函式
-
Promise.all():將多個例項包裝成一個新例項,返回全部例項狀態變更後的結果陣列(齊變更再返回)
- 入參:具有
Iterator介面
的資料結構 - 成功:只有全部例項狀態變成
resolved
,最終狀態才會變成resolved
- 失敗:其中一個例項狀態變成
rejected
,最終狀態就會變成rejected
- 入參:具有
- Promise.race():將多個例項包裝成一個新例項,返回全部例項狀態優先變更後的結果(先變更先返回)
-
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()
-
next():使指標移向下一個狀態,返回
-
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語句
-
next():將
應用場景
- 非同步操作同步化表達
- 控制流管理
- 為物件部署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()可傳值
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");
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})-(?<day>\d{2})/u
- 匹配:
time.replace(regexp, "$<day>/$<month>/$<year>")
- 宣告:
- 形式:
Promise
- finally():指定不管最後狀態如何都會執行的回撥函式
Async
-
非同步迭代器(for-wait-of):,迴圈等待每個
Promise物件
變為resolved狀態
才進入下一步
ES2019
字串擴充套件
-
直接輸入U+2028和U+2029:字串可直接輸入
行分隔符
和段分隔符
- JSON.stringify()改造:可返回不符合UTF-8標準的字串
- trimStart():消除字串頭部空格,返回新字串
- trimEnd():消除字串尾部空格,返回新字串
物件擴充套件
-
Object.fromEntries():返回以鍵和值組成的物件(
Object.entries()
的逆操作)
陣列擴充套件
- flat():扁平化陣列,返回新陣列
- flatMap():對映且扁平化陣列,返回新陣列(只能展開一層陣列)
函式擴充套件
- toString()改造:返回函式原始程式碼(與編碼一致)
-
catch()引數可省略:
catch()
中的引數可省略
Symbol
-
description:返回
Symbol值
的描述
ES提案
宣告
-
globalThis物件:作為頂層物件,指向全域性環境下的
this
-
do表示式:封裝塊級作用域的操作,返回內部最後執行表示式的值(
do{}
) -
throw表示式:直接使用
throw new Error()
,無需()
或{}
包括 - !#命令:指定指令碼執行器(寫在檔案首行)
數值擴充套件
-
數值分隔符(_):使用
_
作為千分位分隔符(增加數值的可讀性) -
BigInt():建立任何位數的整數(新增的資料型別,使用
n
結尾)
物件擴充套件
-
鏈判斷操作符(?.):是否存在物件屬性(不存在返回
undefined
且不再往下執行) -
空判斷操作符(??):是否值為
undefined
或null
,是則使用預設值
函式擴充套件
-
函式部分執行:複用函式功能(
?
表示單個引數佔位符,...
表示多個引數佔位符) -
管道操作符(|>):把左邊表示式的值傳入右邊的函式進行求值(
f(x)
=>x |> f
) -
繫結運算子(::):函式繫結(左邊是物件右邊是函式,取代
bind
、apply
、call
呼叫)- bind:
bar.bind(foo)
=>foo::bar
- apply:
bar.apply(foo, arguments)
=>foo::bar(...arguments)
- bind:
Proxy
-
Promise.try():不想區分是否同步非同步函式,包裝函式為例項,使用
then()
指定下一步流程,使用catch()
捕獲錯誤
Realm
- 定義:提供
沙箱功能
,允許隔離程式碼,防止被隔離的程式碼拿到全域性物件 - 宣告:
new Realm().global
Class
-
靜態屬性:使用
static
定義屬性,該屬性不會被例項繼承
,只能通過類來呼叫 -
私有屬性:使用
#
定義屬性,該屬性只能在類內部訪問 -
私有方法:使用
#
定義方法,該方法只能在類內部訪問 -
裝飾器:使用
@
註釋或修改類和類方法
Module
-
import():動態匯入(返回
Promise
)- 背景:
import命令
被JS引擎靜態分析,先於模組內的其他語句執行,無法取代require()
的動態載入功能,提案建議引入import()
來代替require()
- 位置:可在任何地方使用
- 區別:
require()
是同步載入,import()
是非同步載入 - 場景:按需載入、條件載入、模組路徑動態化
- 背景:
- import.meta:返回指令碼元資訊
Async
-
頂層Await:允許在模組的頂層獨立使用
await命令
(借用await
解決模組非同步載入的問題)
結語
寫到最後總結得差不多了,後續如果我想起還有哪些ES6特性遺漏的,會繼續在這篇文章上補全,同時也希望各位倔友對文章裡的要點進行補充或者提出自己的見解。歡迎在下方進行評論或補充喔,喜歡的點個贊或收個藏,保證你在開發時用得上。
最後送大家一張完整的ES6特性圖,記得給我點個贊喔,算是對我一種鼓勵啦,哈哈!