ES6-ES12
August 31, 2021(January 21, 2023更新)
Content
- ES6 (ECMAScript 2015)
- ES7 (ECMAScript 2016)
- ES8 (ECMAScript 2017)
- ES9 (ECMAScript 2018)
- ES10 (ECMAScript 2019)
- ES11 (ECMAScript 2020)
- ES12 (ECMAScript 2021)
ES6 (ECMAScript 2015)
1. let/const
let
以前在宣告變數都使用全域的var
在 ES6 新增了let
可以更明確的規範變數作用的範圍。
function f() {
let x = 1
{
console.log(x) // 1
let y = 2
}
console.log(y) // Uncaught ReferenceError: y is not defined
}
f()
const 簡化了定義常數的宣告,常數必須初始化且不能變更。
const PI = 3.14
2. arrow functions
var a = [1, 2, 3, 4, 5]
//ES5
a.map(function (v) {
return v + 1
})
//ES6
a.map(v => v + 1)
3. classes
加入 classes,定義 class 更直觀。在 ES5 以前定義物件很複雜,沒有 class 關鍵字,所以會看到很多XXX.prototype.YYY
的語法,在 ES6 幾乎看不見,精簡許多。
- ES6 建立 Stack Class
class Stack {
constructor() {
this.stack = []
}
push(num) {
this.stack.push(num)
}
pop() {
return this.stack.pop()
}
size() {
return this.stack.length
}
}
s = new Stack()
s.push(1)
s.push(2)
s.push(3)
s.pop() // 3
s.size() // 2
- ES6 之前的寫法
const Stack = function () {
this.stack = []
}
Stack.prototype.push = function (num) {
this.stack.push(num)
}
Stack.prototype.pop = function () {
return this.stack.pop()
}
Stack.prototype.size = function () {
return this.stack.length
}
- 繼承寫法
class Animal {
constructor(name, leg) {
this.name = name
this.leg = leg
}
bark() {
console.log("animal bark")
}
}
class Cat extends Animal {
constructor({ name, leg }) {
super(name, leg)
this.run = true
this.fly = false
}
bark() {
console.log("meow")
}
}
let cat = new Cat({ name: "Jack", leg: 4 })
cat // Cat {name: "Jack", leg: 4, run: true, fly: false}
cat.bark() // meow
4. template string
允許在字串中插入變數,不用像以前一樣做字串串接。
let name = "Tom"
let template = `Hello, ${name}.` // "Hello, Tom."
5. destructuring
在拆解陣列或是物件時更方便
let num = [1, 2, 3]
let [a, b, c] = num // a = 1, b = 2, c = 3
let obj = { a: 1, b: 2, c: 3 }
let { a, b, c } = obj
console.log(a, b, c) // 1, 2, 3
6. default + rest + speard
-
default
在傳遞參數允許設定預設值,能夠預防一些例外產生 e.g.
let f = (a = 1, b = 2, c = 3) => {
console.log(a, b, c)
}
f() // 1 2 3
f(4, 5) // 4 5 3
f(4, 5, 6) // 4 5 6
-
rest
在使用函數或是設計函數上可能會遇上不確定到底要幾個參數, ES6 可以讓你在這方面更有彈性。不確定的參數使用
...p
作表示,p 為一個陣列。
let f = (a, ...rest) => {
console.log(a, rest)
}
f(1, 2, 3, 4, 5) // 1 [2, 3, 4, 5]
- spread
let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
let arr3 = [...arr1, ...arr2] // [1, 2, 3, 4, 5, 6]
7. iterators + for..of
let nums = [1, 2, 3, 4]
for (let n of nums) {
console.log(n)
}
/*
1
2
3
4
*/
8. Generators
-
一般function
function f() { console.log('1.') console.log('2.') return 'Hello World' }
-
generator function
會在function保留字後面在加上星號(*)
generator function回傳值是generator object, 是一個iterator
genertaor function內使用yield
來暫停執行。function* f() { yield 'yield 1.' console.log('1.') yield 'yield 2.' console.log('2.') return 'Hello World' } const iter = f() iter.next() // {value: 'yield 1.', done: false} iter.next() // {value: 'yield 2.', done: false} iter.next() // {value: 'Hello World', done: true}
function* f() {
yield 'a'
yield 'b'
yield 'c'
yield 'd'
}
const iter = f()
console.log([...iter]) // [a, b, c, d]
費式數列例子
// Generates an infinite stream of Fibonacci numbers.
// The generator doesn't keep the array of all numbers.
function* fibonacci(): IterableIterator<number> {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
function print(n: number) {
let i = 0;
for (const fib of fibonacci()) {
if (i++ === n) break;
console.log(fib);
}
}
// Print first 10 Fibonacci numbers.
print(10);
-
Reference
9. unicode
s = "\u{1F602}" //"😂"
10. modules
// a.js
const a = () => {
console.log("a.js")
}
module.exports = a
// b.js
import a from "./a"
a() // a.js
11. map/set/weakmap/weakset
-
map / weakmap
Map 的 key 值可以是任意型態(int, string, array)
WeakMap 的 key 值只能是物件
let map = new Map()
map.set(1, "1")
map.set("a", "a")
map.set(["arr1"], ["arr2"])
map // {1 => "1", "a" => "a", ["arr1"] => ["arr2"]}
let weakmap = new WeakMap()
weakmap.set(1, "1")
// Uncaught TypeError: Invalid value used as weak map key
weakmap.set("a", "a")
// Uncaught TypeError: Invalid value used as weak map key
weakmap.set(["arr1"], ["arr2"]) // {["arr1"] => ["arr2"]}
- set / weakset
Set 與 WeakSet 差異: Set 能夠儲存隨意型態(int, string, array), WeakSet 只能夠儲存物件。
let set = new Set()
set.add(1).add(2).add(5) // {1, 2, 5}
set.add(2) // {1, 2, 5}
set.add("2") // // {1, 2, 5, "2"}
set.add([1, 2, 3]) // {1, 2, 5, "2", [1,2,3]}
set.size // 5
let weakset = new WeakSet()
weakset.add(1)
// Uncaught TypeError: Invalid value used in weak set
weakset.add([1, 2, 3]) // {[1,2,3]}
weakset.add([1, 2, 3]) // {[1,2,3], [1,2,3]}
12. proxying/refection
13. symbols
symbol 是 ES6 新增的一種基本數據型態(primitive data type)。
基本型態有七種: string,number,bigint,boolean,null,undefined,symbol。
特性: 唯一且不可改變(immutable)的資料型態,可用來識別物件的屬性。
let a = Symbol("123")
let b = Symbol("123")
a === b // false
let a = Symbol("aaa")
let b = Symbol("bbb")
let obj = { [a]: 123, [b]: 456 }
obj // {Symbol(aaa): 123, Symbol(bbb): 456}
14. promises
程式常常需要做 request 到後端來取得資料,但 server 並不會馬上回傳資料,要等待 server 處理的時間。這時程式就必須停下來等待,造成時間浪費。
ES6 提出了 promise 來解決這個非同步(asynchronously)的狀況。當發送 request 到 server 後,並不會停下來等 server 處理完,而是留下 callback 後,繼續執行後面任務。
當 server 成功處理好拿到資料準備回傳時,再去執行剛剛留下的 callback function。留下的 callback function 有分為 request 呼叫成功(resolve)與 request 呼叫失敗(reject)。
const callServer = time => {
return new Promise((resolve, reject) => {
if (time <= 300) {
setTimeout(() => resolve({ msg: "request success!" }), time)
} else {
setTimeout(() => reject({ msg: "request failed!" }), time)
}
})
}
callServer(200).then(res => console.log(res))
// {msg: "request success!"}
callServer(400)
.then(res => console.log(res))
.catch(e => console.log(e)) // <--
// {msg: "request failed!"}
15. binary and octal literals
支援二進位與八進位表示
0b111 //7
0o123 //83
let num = "1010"
let binNum = `0b${num}`
let decNum = BigInt(binNum) // 10n
decNum.toString(2) // "1010"
ES7 (ECMAScript 2016)
1. Exponentiation Operator
2 ** 3 // 8
2. Array.includes()
let arr = ["Tom", "Jack", "David"]
arr.includes("Tom") // true
ES8 (ECMAScript 2017)
1. Async functions (MDN)
可以用來簡化 ES6 Promise 寫法,await 表達式只能在 async 函式內部使用,await 會等待 Promise 解析完畢才會繼續往下執行。
之前 ES6 Promise 範例,在 callServer()之後要處理回傳的 Promise 需要使用.then()
來接。若拿回來的資料還要當做其他 function 的 input 會遇到.then().then().then()
的狀況。
const callServer = time => {
return new Promise((resolve, reject) => {
if (time <= 300) {
setTimeout(() => resolve({ msg: "request success!" }), time)
} else {
setTimeout(() => reject({ msg: "request failed!" }), time)
}
})
}
callServer(200).then(res => console.log(res))
// {msg: "request success!"}
callServer(400)
.then(res => console.log(res))
.catch(e => console.log(e)) // <--
// {msg: "request failed!"}
ES8 async function 可以改善處理 promise 的寫法。
const callServer = time => {
return new Promise((resolve, reject) => {
if (time <= 300) {
setTimeout(() => resolve({ msg: "request success!" }), time)
} else {
setTimeout(() => reject({ msg: "request failed!" }), time)
}
})
}
const run = async () => {
let success = await callServer(200)
let error = {}
try {
error = await callServer(400)
} catch (err) {
error = err
}
console.log(success) // {msg: "request success!"}
console.log(error) // {msg: "request failed!"}
}
run()
2. Object.entries
let obj = { a: 1, b: 2, c: 3 }
for (let [key, value] of Object.entries(obj)) {
console.log(key, value)
}
/*
a 1
b 2
c 3
*/
3. Object.values
let obj = { a: 1, b: 2, c: 3 }
for (let value of Object.values(obj)) {
console.log(value)
}
/*
1
2
3
*/
4. Object.getOwnPropertyDescriptors
let obj = { a: 1, b: 2, c: 3 }
let descriptors = Object.getOwnPropertyDescriptors(obj)
descriptors
/*
{
a: {value: 1, writable: true, enumerable: true, configurable: true}
b: {value: 2, writable: true, enumerable: true, configurable: true}
c: {value: 3, writable: true, enumerable: true, configurable: true}
}
*/
5. String.padStart() (MDN), String.padEnd() (MDN)
-
String.padStart()
str.padStart(targetLength [, padString])
"101".padStart(8) // " 101"
"101".padStart(8, "0") // "00000101"
"101".padStart(8, "xyz") // "xyzxy101"
- String.padEnd()
"101".padEnd(8) // "101 "
"101".padEnd(8, "0") // "10100000"
"101".padEnd(8, "xyz") // "101xyzxy"
6. Shared memory and Atomic
Atomic 是類似鎖,當 CPU1 在讀寫 shared memory 時,會把 shared memory 中的值複製一份到 cache 中。在讀寫時把 shared memory 鎖住,讓其他 CPU 不能夠讀寫,避免複製到舊的值,確保資料一致性。
ES9 (ECMAScript 2018)
1. Object rest and spread
在 ES6 加入的 rest 與 spread 只能針對陣列做展開,ES8 擴展到也能夠針對物件展開。
let obj = { a: 1, b: 2, c: 3 }
let { a, ...rest } = obj
a // 1
rest // {b: 2, c: 3}
2. Promise.finally
不管 Promise 是 resolve 還是 reject 都會執行 finally(),可以用來關閉 loading 狀態。
const fetchData = time => {
return new Promise((resolve, reject) => {
if (time <= 300) {
setTimeout(() => resolve({ data: "request success" }), time)
} else {
setTimeout(() => reject({ data: "request failed!" }), time)
}
})
}
const run = async () => {
fetchData(400)
.then(({ data }) => console.log(data))
.catch(err => console.log(err))
.finally(() => console.log("finally"))
}
run()
// {data: "request failed!"}
// finally
3. Asynchronous Iteration
支援在 for 迴圈內呼叫非同步 function。
const fetchData = time => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(`success: ${time}ms`), time)
})
}
const run = async () => {
let times = [500, 1000, 2000]
for await (let ms of times) {
fetchData(ms).then(res => console.log(res))
}
}
run()
// success: 500ms
// success: 1000ms
// success: 2000ms
4. Regular Expression Features
- Named capture groups
在正規表達式內使用?<name>
來幫 group 取名字
const re = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/
const match = re.exec("2021-09-05")
match.groups // {year: "2021", month: "09", day: "05"}
-
dotAll
tc39/proposal-regexp-dotall-flag.
在正規表達式中代表任意字元,但不能是換行符號(line terminator)/foo.bar/.test("fooAbar") // true /foo.bar/.test("foo\nbar") // false
建議解決方案
增加 s flag,讓.
能夠匹配任何字元包含換行符號/foo.bar/s.test("foo\nbar") // true
-
lookbehind assertions
tc39/proposal-regexp-lookbehind-
Positive
(?<=...)
(?<=x)y
表示 x 後面接著 y,才會匹配 ylet re = /(?<=\$)\d+\.\d+/ re.exec("$10.53") // ["10.53", index: 1, input: "$10.53", groups: undefined] re.exec("€10.53") // null
- Negative
(?<!...)
(?<!x)y
表示 x 後面不是 y,才會匹配 y
-
-
Unicode property escapes
tc39/proposal-regexp-unicode-property-escapesconst regexGreekSymbol = /\p{Script=Greek}/u regexGreekSymbol.test("π") // true
5. Escape sequences allowed in tagged template literals
const tagged = (s, version, year) => {
console.log(s)
console.log(version)
console.log(year)
}
let version = "ES9"
let year = 2018
console.log(tagged`This is ${version} (ECMAScript${year}).`)
/*
["This is ", " (ECMAScript", ").", raw: Array(3)]
ES9
2018
*/
ES10 (ECMAScript 2019)
1. Array.flat() (MDN) / Array.flatMap() (MDN)
- flat(deep)
let arr1 = [1, 2, [3, 4]]
arr1.flat() // [1, 2, 3, 4]
let arr2 = [1, 2, [3, 4, [5, 6]]]
arr2.flat() // [1, 2, 3, 4, [5, 6]]
arr2.flat(2) // [1, 2, 3, 4, 5, 6]
let arr3 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]]
arr3.flat(Infinity) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- flatMap()
和 map()差在 map()完會再經過一層的 flat()展開。
2. Object.fromEntries()(MDN)
把 Map、Array 傳換成物件。
const map = new Map([
["a", 1],
["b", 2],
])
map // Map(2) {'a' => 1, 'b' => 2}
Object.fromEntries(map) // {a: 1, b: 2}
let arr = [
["a", 1],
["b", 2],
["c", 3],
]
Object.fromEntries(arr) // {a: 1, b: 2, c: 3}
3. String.trimStart() / String.trimEnd()
去除左邊/右邊空白並返回一個新的字串,不會直接修改原來的值。
let s = " hello "
s.trimStart() // 'hello '
s.trimEnd() // ' hello'
ES11 (ECMAScript 2020)
1. string.matchAll() (MDN)
let s = "abcdacd"
console.log([...s.matchAll(/a/g)])
/*
[
['a', index: 0, input: 'abcdacd', groups: undefined],
['a', index: 4, input: 'abcdacd', groups: undefined]
]
*/
2. BigInt
遇到類似Leetcode 1985這類問題, Number 型態已經不足夠表示。Number 型態表示上限為。
Math.pow(2, 53) // 9007199254740992
Number.MAX_SAFE_INTEGER // 9007199254740991
Number.MAX_SAFE_INTEGER + 1 // 9007199254740992
Number.MAX_SAFE_INTEGER + 2 // 9007199254740992
BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1) // 9007199254740992n
BigInt(Number.MAX_SAFE_INTEGER) + BigInt(2) // 9007199254740993n
3. Promise.allSettled
Promise.all() 在遇到 reject 時會停止後續動作(short-circuit),直接噴錯。
Promise.allSettled() 其中有遇到 reject 還是會將其他 promise 執行完畢。
let promise1 = Promise.resolve(3)
let promise2 = new Promise((resolve, reject) => {
setTimeout(reject("error"))
})
let promise3 = 42
Promise.all([promise1, promise2, promise3])
.then(values => {
console.log(values)
})
.catch(() => console.log("error"))
// error
Promise.allSettled([promise1, promise2, promise3]).then(values => {
console.log(values)
})
/*
[
{ status: 'fulfilled', value: 3 },
{ status: 'rejected', reason: 'error' },
{ status: 'fulfilled', value: 42 }
]
*/
4. globalThis
var a = 10 // Window{..., a: 10, ...}
(function () {
let a = 20
console.log(a) // 20
console.log(this.a) // 10
console.log(window.a) // 10
console.log(frames.a) // 10
console.log(globalThis.a) // 10
})()
5. Nullish coalescing Operator (??) (MDN)
??
左側為否定只判斷null
和undefined
console.log(null ?? "hello") // hello
console.log(undefined ?? "hello") // hello
console.log(false ?? "hello") // false
console.log(0 ?? "hello") // 0
console.log("" ?? "hello") // ""
console.log(null || "hello") // hello
console.log(undefined || "hello") // hello
console.log(false || "hello") // hello
console.log(0 || "hello") // hello
console.log("" || "hello") // hello
6. Optional Chaining (?.)
let a = {
b: {
c: 1,
},
}
a.b.c // 1
a.d.c // TypeError: Cannot read properties of undefined
a?.d?.c // undefined
7. module
- import()
支援動態載入,可在需要時才 import。 - import.meta
- export * as ns from ‘module’
ES12 (ECMAScript 2021)
1. replaceAll
let str = "ABCDFABCGHIABC"
str.replaceAll("ABC", "xxx") // 'xxxDFxxxGHIxxx'
str.replace(/ABC/g, "xxx") // 'xxxDFxxxGHIxxx'
2. Promise.any
a Promise combinator that short-circuits when an input value is fulfilled (遇到的第一個 Promise 是 fullilled 就回傳,後面的都不做了)
-
Promise.all
所有的 Promise 都要 fulfilled 否則報錯
-
Promise.allSettled
遇到 Promise 是 rejected 還是會把後面的 Promise 做完
-
Promise.any
會傳第一個 fulfilled 的 Promise,之後的 Promise 不執行。
若全部都是 rejected 則報錯(Uncaught (in promise) AggregateError: All promises were rejected
)
3. AggregateError
a new Error type to represent multiple errors at once (一個新的 Error type, 把多個 Errors 包在一起)
Promise.any([
Promise.reject("error1"),
Promise.reject("error2"),
Promise.reject("error3"),
]).catch(err => err)
/*
AggregateError{
errors: (3) ['error1', 'error2', 'error3']
message: "All promises were rejected"
stack: "AggregateError: All promises were rejected"
}
*/
4. ??=
, &&=
, ||=
let a = undefined
let b = 1
a ??= b // 如果a是nullish(null, undefined), 將b賦值到a
let a = 1
a &&= 10 // 如果a是truthy,則賦值到a (a = 10)
let b = 0
b &&= 10 // 如果b是falsy,則不賦值 (b = 0)
let a = 1
a ||= 10 // 如果a是truthy,則不賦值 (a = 0)
let b = 0
b ||= 10 // 如果b是falsy,則賦值到b (b = 10)
5. WeakRef
for referring to a target object without preserving it from garbage collection
6. FinalizationRegistry
to manage registration and unregistration of cleanup operations performed when target objects are garbage collected (在註冊的物件被回收時,執行 callback)
const r = new FinalizationRegistry(
() => console.log("callback")
);
(() => {
let obj = { a: 1, b: 2 }
r.register(obj)
})();