文章目錄
前言
阮一峯老師的 ES6 入門教程 看了好幾遍,發現每次看完都感覺有很多地方不懂,越看越焦慮,現在稍微明白了,這可能是大多數人追求的完美主義,希望每件事情都搞懂心理才踏實。其實,是沒必要的,根據”二八定律“,我們只要掌握其中的 20% 常用知識和語法去解決開發中的 80% 問題就夠了,剩下不懂的地方可以通過閱讀相關文檔和資料解決。更重要的原因是前端技術更新迭代如此之快,我們也要跟上步伐,把有限的時間放在正確的事情上,更何況我們還有詩和遠方。所以在這裏總結一下 ES6 常用語法和開發過程中遇到的一些坑,然後繼續前行。
let 聲明 VS const 聲明
相同點
- 沒有變量提升,必須先聲明後使用,否則報錯
- 形成塊級作用域,暫時性死區
- 不能重複聲明
- 在全局聲明,不能掛載在window上
不同點
const
聲明變量時需要賦初始值const
聲明的基本類型數值不能被修改,聲明的引用類型可以修改屬性,但是不能重新賦值改變引用的地址- 使用
const
程序執行效率更快
console.log(a) // 報錯,Uncaught ReferenceError: a is not defined
let a = 1
let a = 2 // 報錯,Uncaught SyntaxError: Identifier 'a' has already been declared
if (true) { // 形成塊級作用域
var b = 33
let c = 22
}
console.log(b) // 33
console.log(c) // Uncaught ReferenceError: c is not defined
var name = '張三'
let lisi = '李四'
console.log(window.name) // 張三
console.log(window.lisi) // undefined
const d = '11'
const obj = {name: 33}
d = '22' // Uncaught TypeError: Assignment to constant variable.
obj.name = '2323' // {name: 2323}
obj = {} // Uncaught TypeError: Assignment to constant variable
解構賦值
- 數組解構賦值
- 對象解構賦值
- 字符串解構賦值
- 函數參數解構賦值
解構賦值使用場景
- 交換變量的值
- 從函數返回多個值
- 函數參數解構
- 提取 JSON 數據
- 遍歷 Map 結構
- 模塊導入解構
let x = {
name: 33
}
ley y = [33, 22]
//交換值
[x, y] = [y, x]
console.log(x) // [33, 22]
function fn() {
...
return {name, value}
}
const {name, value} = fn() // 提取函數多個返回值
function getValue({name, value, ...}) { // 參數解構賦值
...
return value
}
let {name, data } = {name, '33', value: 22, data: [3, 2, 4]} //對象解構賦值,提取 JSON數據
function fn () {
for(let [k, v] of map.entries()) { // [k, v] map 解構賦值
...
}
}
import {module1, module2, ... } from './filename.js' // 模塊解構賦值
注意事項
-
解構賦值變量重命名。有時候我們需要轉換一下後臺返回的鍵名,這時這個地方用的上
-
函數參數對象解構賦值時,我們需要給它默認值,這時如果調用函數不傳對象就會報錯,解決方法是初始化一個空對象
let {x: width, y: height} = {x: '220px', y: '100px'} // 重命名
console.log(width, height) // 220px 100px
function fn ({name='', value=0}) {
...
}
fn() // Uncaught TypeError: Cannot read property 'name' of undefined
// 加一個空對象
function fn ({name='', value=0} = {}) {
...
}
字符串新增方法
-
includes
:返回布爾值,判斷字符串是否包含某串字符。接受兩個參數,第二個參數是起始的位置 -
startsWith
:返回布爾值,判斷字符串開頭是否包含某串字符 -
endsWith
:回布爾值,判斷字符串結尾是否包含某串字符 -
repeat
:返回一個新的字符串,將原字符串重複 n 次 -
padStart
:返回新的字符串,在字符串前面補全,接受兩個參數,第一個參數補全字符串長度,第二個參數補全的字符 -
padEnd
::返回新的字符串,在字符串後面補全,接受兩個參數,第一個參數補全字符串長度,第二個參數補全的字符 -
trimStart
:去除前面空格 -
tiemEnd
:去除後面空格 -
matchAll
: 返回一個正則表達式在當前字符串的所有匹配
let str = 'abcdef'
str.includes('bc') // true
str.includes('ss') // false
str.repeat(2) // "abcdefabcdef"
str.repeat(0) // ''
str.repeat(-1) // 報錯,Uncaught RangeError: Invalid count value
str.padStart(10, 1) // "1111abcdef"
str.padEnd(10, 0) // "abcdef0000"
正則表達式
方法
-
match
:匹配成功返回數組,匹配不成功返回null -
replace
:替換字符串,返回一個新的字符串 -
search
:匹配返回第一個索引的位置,匹配不成功返回 -1 -
split
:返回數組 -
exec
:匹配成功返回數組,匹配不成功返回null -
test
:返回布爾值 boolean
數值擴展
方法
Number.isInteger
:判斷是否是整數Number.isSafeInteger
:判斷是否是安全整數Number.isNaN
Math.trunc()
:去除一個數的小數部分
函數
函數拓展
-
函數默認參數
-
rest
參數 -
函數
length
屬性。它表示函數參數的個數,如果參數設置默認值,則length
減 1;rest
參數也不會計入length
屬性 -
箭頭函數注意事項
- 函數體內的
this
對象,就是定義時所在的對象 - 箭頭函數不可以當作構造函數使用,不可以使用
new
命令 - 不可以使用
arguments
對象 - 不可以使用
yield
命令 - 箭頭函數不適合使用場景:1、定義對象的方法;2、監聽事件回調
- 函數體內的
-
尾調用優化
-
尾遞歸
function fn(a, b, c) {
...
}
fn.leng // 3
function fn(a, b=1, c=2) {
...
}
fn.leng // 1
let obj = {
name: 'JEFFER',
getName: () => { // 不適合箭頭函數
console.log(this.name)
}
obj.getName() // undefined
doc.addEventListener('click', () => {
console.log(this) // 這裏的this原本指向dom元素,現在使用箭頭函數指向了 window
}
數組
-
from
:將Arguments
、DOM
元素集合等僞數組,轉化爲數組 -
of
:將多個數據項向轉化爲數組 -
find
:查找元素,查找成功返回數組元素,查找不到返回null
-
findIndex
:查詢下標,查找成功返回數組元素下標,查找不到返回 -1 -
copyWithin
:數組內部複製 -
fill
:填充數組 -
entries
:返回數組下標和元素組成的二維數組 -
keys
:返回下標的數組 -
values
:返回元素的數組 -
includes
:匹配數組項,返回boolean
-
flat
: 數組降維
function fn(a, b,c) {
...
console.log(Array.from(Arguments)) // [a, b, c]
}
Array.of(1, 2, 3) // [1, 2, 3]
let arr = [22, 33, 44, 55]
arr.find((item) => item === '33') //33
arr.findIndex((item) => item === '33') // 1
數組迭代方法
數組迭代方法在函數式編程時非常有用,熟練使用函數式編程能大大提高自己的編程效率,寫出的代碼也很簡潔,易讀易維護
-
forEach
:遍歷數組,中間可以使用continue
跳過本次循環,在循環中break
關鍵字是不生效的 -
filter
:過濾數組返回一個新數組,不改變原數組。可以進行鏈式操作 -
map
:數組映射,返回一個數組,不改變原數組。可以進行鏈式操作 -
reduce
:數組彙總,返回一個值。接收兩個參數,第二個參數接收默認值 -
every
:返回一個boolean
值,遍歷結果全部爲true
才爲true
,否則返回false
-
some
:返回一個boolean
值,遍歷某次結果返回true
時會立刻結束循環。這個想中間結束循環很有用,不用再借助break
結束循環,有利於提高執行效率
let arr = [32,32,2324, 223]
arr.forEach((item, index) => {
if (...) {
continue // 滿足條件想盡早結束本次循環
}
})
arr.filter((item, index) => {
...
return item
}).filter(()=>{...}).map(()=>{...}) // 鏈式操作
arr.map((item, index) => {
...
return item
}).filter(()=>{...}).map(()=>{...}) // 鏈式操作
arr.reduce((pre, next) { // 彙總
...
return item
}, [])
arr.ervery((pre, next) {
...
return item
})
arr.some((pre, next) {
if (...) {
...
return true // 結束循環
}
...
return item
})
對象
方法
-
Object.assign()
:合併對象,淺拷貝,改變原對象 -
Object.keys()
:返回數組,獲取對象的鍵 -
Object.values()
:返回數組,獲取對象的值 -
Object.entries()
:返回二維數組,獲取對象的鍵值 -
Object.fromEntries()
:將二維數組或 map 數據類型轉化爲對象 -
Object.create()
:創建對象 -
Object.is()
:同值相等,NaN
等於NaN
,0
不等於-0
-
Object.getPrototypeOf(obj)
:獲取對象原型 -
Object.setPrototypeOf(obj)
:設置對象原型
Object.entries()
和 Object.fromEntries()
方法可以相互轉換
Object.fromEntries
特殊用法,配合 URLSearchParams
對象,將查詢字符串轉爲對象
le obj = { name: '33' }
Object.assign(obj, {value: 11}) // 注意,改變了原對象obj
console.log(obj) // {name, '33', value: 11}
// 二維數組轉對象 { foo: "bar", baz: 42 }
Object.fromEntries([
['foo', 'bar'],
['baz', 42]
])
// map 轉對象
const entries = new Map([
['foo', 'bar'],
['baz', 42]
]);
Object.fromEntries(entries) // { foo: "bar", baz: 42 }
// 將 URL 參數轉化爲對象
Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))
// { foo: "bar", baz: "qux" }
Set 和 Map 數據結構
Set
Set
類似於數組結構,存放唯一值
屬性
Set.prototype.constructor
:構造器Set.prototype.size
:Set
實例的成員總數
方法
Set.prototype.add(value)
:添加某個值,返回 Set 結構本身。Set.prototype.delete(value)
:刪除某個值,返回一個布爾值,表示刪除是否成功。Set.prototype.has(value)
:返回一個布爾值,表示該值是否爲Set
的成員。Set.prototype.clear()
:清除所有成員,沒有返回值。
s.add(1).add(2).add(2);
// 注意2被加入了兩次
s.size // 2
s.has(1) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
迭代方法
Set.prototype.keys()
:返回鍵名的遍歷器Set.prototype.values()
:返回鍵值的遍歷器Set.prototype.entries()
:返回鍵值對的遍歷器Set.prototype.forEach()
:使用回調函數遍歷每個成員
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// 這樣也行
for (let item of set) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
set.forEach((value, key) => console.log(key + ' , ' + value))
// "red", "red"
// "green", "green"
// "blue", "blue"
使用場景
- 可以用來去重,數組去重、字符串去重
- 實現並集、交集、差集
// 去除數組的重複成員
[...new Set(array)]
// 去除字符串中重複字符
[...new Set('ababbc')].join('')
// "abc"
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 並集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
注意事項
- 添加
Set
集合數據區分數據類型,5
和“5”
兩個值不一樣 Set
中NaN
是相等的,多次添加只添加一個- 兩個對象總不相等
Set
和Array
可以相互轉換
let set = new Set()
set.add(5)
set.add('5')
set.add(NaN)
set.add(NaN)
set.add({})
set.add({})
set.size // 5
// set 最終結果是
// Set(5) {5, "5", NaN,{},{}}
// 數組轉 Set
let set1 = new Set(arr)
// Set 轉數據
let arr = [...set]
// 或者
let arr = Array.from(set)
Map
Map
類似於對象結構,本質上是鍵值對,簡稱 Hash 結構
屬性
Map.prototype.constructor
:構造器Map.prototype.size
:Map
實例的成員總數
方法
-
Map.prototype.set(key, value)
:添加某個鍵值對,返回 整個Map 結構。如果key
已經有值,則鍵值會被更新,否則就新生成該鍵。(添加多個可以進行鏈式操作) -
Map.prototype.get(key)
:讀取key
對應的鍵值,如果找不到key
,返回undefined
-
Map.prototype.has(key)
:返回一個布爾值,表示某個鍵是否在當前 Map 對象之中 -
Map.prototype.delete(key)
:刪除某個鍵,返回true
。如果刪除失敗,返回false
-
Map.prototype.clear()
:清除所有成員,沒有返回值。
迭代方法
用法和 Set
一樣,就不粘貼代碼了
-
Map.prototype.keys()
:返回鍵名的遍歷器 -
Map.prototype.values()
:返回鍵值的遍歷器 -
Map.prototype.entries()
:返回鍵值對的遍歷器 -
Map.prototype.forEach()
:使用回調函數遍歷每個成員,forEach
可以接受第二個參數用來綁定this
執行上下文
Map 和數組的轉換
數組給我們提供了豐富的 API
,將 Map
結構轉換爲數組處理數據更方便
const map = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c');
[...map.keys()]
// [1, 2, 3]
[...map.values()]
// ['a', 'b', 'c']
[...map] or [...map.entries]
// [[1,'a'], [2, 'b'], [3, 'c']]
const map1 = new Map(
[...map].filter(([k, v]) => k < 3)
);
// 產生 Map 結構 {1 => 'a', 2 => 'b'}
const map2 = new Map(
[...map].map(([k, v]) => [k * 2, '_' + v])
);
// 產生 Map 結構 {2 => '_a', 4 => '_b', 6 => '_c'}
Proxy 攔截器
-
get(target, propKey, receiver):攔截對象屬性的讀取,比如
proxy.foo
和proxy['foo']
。 -
set(target, propKey, value, receiver):攔截對象屬性的設置,比如
proxy.foo = v
或proxy['foo'] = v
,返回一個布爾值。 -
has(target, propKey):攔截
propKey in proxy
的操作,返回一個布爾值。 -
deleteProperty(target, propKey):攔截
delete proxy[propKey]
的操作,返回一個布爾值。 -
ownKeys(target):攔截
Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
、for...in
循環,返回一個數組。該方法返回目標對象所有自身的屬性的屬性名,而Object.keys()
的返回結果僅包括目標對象自身的可遍歷屬性。 -
getOwnPropertyDescriptor(target, propKey):攔截
Object.getOwnPropertyDescriptor(proxy, propKey)
,返回屬性的描述對象。 -
defineProperty(target, propKey, propDesc):攔截
Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一個布爾值。 -
preventExtensions(target):攔截
Object.preventExtensions(proxy)
,返回一個布爾值。 -
getPrototypeOf(target):攔截
Object.getPrototypeOf(proxy)
,返回一個對象。 -
isExtensible(target):攔截
Object.isExtensible(proxy)
,返回一個布爾值。 -
setPrototypeOf(target, proto):攔截
Object.setPrototypeOf(proxy, proto)
,返回一個布爾值。如果目標對象是函數,那麼還有兩種額外操作可以攔截。 -
apply(target, object, args):攔截 Proxy 實例作爲函數調用的操作,比如
proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。 -
construct(target, args):攔截 Proxy 實例作爲構造函數調用的操作,比如
new proxy(...args)
。
Reflect
Reflect
是 ES6
爲了操作對象而提供的新 API
每個新東西的出現都是爲了解決實際問題,Reflect
的出現同樣也是爲了解決以下問題:
-
修改某些
Object
方法的返回結果,讓其變得更合理。比如,Object.defineProperty(obj, name, desc)
在無法定義屬性時,會拋出一個錯誤,而Reflect.defineProperty(obj, name, desc)
則會返回false
。 -
讓
Object
操作都變成函數行爲。某些Object
操作是命令式,比如name in obj
和delete obj[name]
,而Reflect.has(obj, name)
和Reflect.deleteProperty(obj, name)
讓它們變成了函數行爲。 -
Reflect
對象的方法與Proxy
對象的方法一一對應,只要是Proxy
對象的方法,就能在Reflect
對象上找到對應的方法
方法
Reflect.apply(target, thisArg, args)
:等同於Function.prototype.apply.call(func, thisArg, args)
,用於綁定this
對象後執行給定函數
const ages = [11, 33, 12, 54, 18, 96];
// 舊寫法
const youngest = Math.min.apply(Math, ages);
// 新寫法
const youngest = Reflect.apply(Math.min, Math, ages);
-
Reflect.construct(target, args)
:等同於new target(...args)
,這提供了一種不使用new
,來調用構造函數的方法。 -
Reflect.get(target, name, receiver)
:獲取對象的值,例如target[name]
,receiver
參數可以修改讀取this
上下文(後面一樣,就不介紹了) -
Reflect.set(target, name, value, receiver)
:設置對象的值target[name] = value
-
Reflect.defineProperty(target, name, desc)
:相當於 -
Reflect.deleteProperty(target, name):
等同於delete target[name]
,用於刪除對象的屬性 -
Reflect.has(target, name)
:等同於name in target
裏面的in
運算符 -
Reflect.ownKeys(target)
:等同於Object.keys(target)
-
Reflect.isExtensible(target)
:等同於Object.isExtensible(target)
-
Reflect.preventExtensions(target)
:等同於Object.preventExtensions(target)
-
Reflect.getOwnPropertyDescriptor(target, name)
:等同於Object.getOwnPropertyDescriptor(target)
-
Reflect.getPrototypeOf(target)
:等同於Object.getPrototypeOf(target)
-
Reflect.setPrototypeOf(target, prototype)
:等同於Object.setPrototypeOf(target)
Promise
promise
是實現異步編程的一種解決方案,可以通過鏈式操作,解決回調地獄的問題
使用場景
- 加載圖片
- 封裝 ajax 請求接口
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
封裝ajax
請求接口
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出錯了', error);
});
方法
-
Promise.prototype.then
:fullfilled
成功狀態之後的回調函數 -
Promise.prototype.catch()
:發生錯誤時的回調函數 -
Promise.prototype.finally()
:該方法是 ES2018 引入標準,不管是成功或失敗都會執行 -
Promise.all()
:用於將多個 Promise 實例,包裝成一個新的 Promise 實例,
const p = Promise.all([promise1, promise2, promise3]).then(res => {});
上面promise1
、promise2
、promise3
都是Promise
實例,它們都是成功之後才能執行 then
回調函數,返回參數 res
是個數組,可以用來解決同時請求多個接口
-
Promise.race()
:和上面的all
方法用法一樣,不同的地方在於只要有一個實例執行成功,它就成功了,只要有一個失敗它也代表失敗了 -
Promise.allSettled()
:該方法和上面all
和race
用法一樣,它主要解決的是不管成功或失敗都返回,狀態總是fulfilled
,不會變成rejected
,該方法由 ES2020 引入,有時候,我們不關心異步操作的結果,只關心這些操作有沒有結束。這時,它就很有用 -
Promise.any()
:和race
很相似,不同的地方,是不會因爲某個 Promise 變成rejected
狀態而結束,有點類似於數組中的Array.prototype.some
方法,該方法目前是一個第三階段的提案 。 -
Promise.resolve()
:Promise
的靜態方法,它的使用場景是將現有對象轉爲 Promise 對象,返回fullfilled
成功狀態 -
Promise.reject()
:Promise
的靜態方法,它的使用場景是將現有對象轉爲 Promise 對象,返回rejected
失敗狀態
Async 函數
async
函數也是異步編程的一種解決方案,它和 Promise
不同在於可以使用同步的編碼方式實現異步編程,除此之外使用它代替 Promise
多次調用 then
方法也會使代碼更容易讀懂和解耦
async
函數是 Generator
函數的語法糖,它在此基礎上做了幾點改進:
-
內置執行器:
async
函數自帶執行器 -
更好的語義:
async
和await
,比起星號和yield
,語義更清楚了 -
更廣的適用性:
async
函數的await
命令後面,可以是 Promise 對象和原始類型的值(數值、字符串和布爾值,但這時會自動轉成立即 resolved 的 Promise 對象)。 -
返回值是 Promise:可以用
then
方法指定下一步的操作
async
函數完全可以看作多個異步操作,包裝成的一個 Promise 對象,而await
命令就是內部then
命令的語法糖
基本用法
const asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
使用場景
- 函數聲明可以使用
- 函數表達式可以使用
- 對象方法可以使用
- Class 方法可以使用
- 迭代數組或對象中使用
- 併發請求接口數據
// 函數聲明
async function foo() {}
// 函數表達式
const foo = async function () {};
// 對象的方法
let obj = { async foo() {} };
obj.foo().then(...)
// 迭代數組
async function dbFuc(db) {
let docs = [{}, {}, {}];
for (let doc of docs) {
await db.post(doc);
}
}
// 雙重 async/await 併發請求接口數據
async function logInOrder(urls) {
// 併發讀取遠程URL
const textPromises = urls.map(async url => {
const response = await fetch(url);
return response.text();
});
// 按次序輸出
for (const textPromise of textPromises) {
console.log(await textPromise);
}
}
Class
class
是一個語法糖,它本身指向一個構造函數
使用場景
- 使用
class
可以實現面向對象編程 - 可以使用
extends
繼承,實現抽象和代碼複用 - 可以定義內部屬性和靜態方法,實現封裝
Module 模塊
作用
模塊的作用是可以將一個大的程序拆分成相互依賴的小文件,再用簡單的方法拼接起來,可以讓程序代碼更容易維護、擴展功能、閱讀和調試。
ES6 VS 其他模塊
- CommonJS 模塊輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用。
- CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口。
注意
ES6
引入的是隻讀操作,最好不要修改它的值
基本用法
-
export
命令輸出,後面必須是var, let, const
聲明的變量、函數或對象 -
export default
輸出,後面可以是函數、對象或其他數據類型值,不能有var, let, const
關鍵字,並且一個文件只能有一個export default
-
import
命令輸入,export
輸出的要加大括號,export default
輸出的不用加大括號
// a.js
var name = 33
export name; // 報錯
export var firstName = 'Michael'; // 正確
export default name // 正確
function fn () {}
export fn; // 報錯
export default fn 正確
export function fn () {} // 正確
export default function fn () {} // 正確
export { // 正確
name as other, // 重命名
firstName
}
export default { // 正確
name,
firstName
}
//-----------------------
// b.js 文件
import { name as username } from './a.js' // export 導出的、重命名
import * as all from './a.js' // 導入所有
import name fromm './a.js // export default 導出的
import() 函數
ES2020提案 引入import()
函數,支持動態加載模塊,import()
函數可以用在任何地方,不僅僅是模塊,非模塊的腳本也可以使用。它是運行時執行,也就是說,什麼時候運行到這一句,就會加載指定的模塊
編程風格
塊級作用域
- 聲明變量用
let
代替var
,形成塊級作用域,沒有副作用 let
和const
優先使用const
,const
聲明常量,減少由於修改值出錯,同時有利於提高程序的運行效率
解構賦值
- 從數組、對象中提取值時使用解構賦值
- 函數參數對象使用解構賦值
- 函數返回對象使用解構賦值
函數
- 能使用箭頭函數的地方儘量使用箭頭函數
- 使用函數式編程