ES6-ES12簡單知識點總結
1.ES6相關知識點
1.1.物件字面量的增強
ES6中對物件字面量的寫法進行了增強,主要包含以下三個方面的增強:
- 屬性的簡寫:當給物件設定屬性時,如果希望變數名和屬性名一樣就可以直接寫該變數名;
- 方法的簡寫:物件中的方法可直接寫成
foo() {}
的形式; - 計算屬性名:物件的屬性名可以動態傳入,將變數使用
[]
包裹即可;
const obj = {
// 1.屬性簡寫
name,
age,
// 2.方法簡寫
foo() {
console.log('foo')
},
// 3.計算屬性名
[key]: 'value'
}
1.2.解構
為了方便從陣列或物件中獲取資料,ES6給我們提供瞭解構的方案,可分為陣列的解構和物件的解構。
-
陣列的解構:注意陣列的解構是按元素順序來的。
const names = ['curry', 'kobe', 'klay', 'james'] // 1.基本的解構,解構出陣列所有的元素 var [name1, name2, name3, name4] = names console.log(name1, name2, name3, name4) // curry kobe klay james // 2.解構部分元素,只解構後面兩個元素 var [, , name3, name4] = names console.log(name3, name4) // klay james // 3.解構出第一個元素,後面的元素放到一個新陣列中 var [name1, ...newNames] = names console.log(newNames) // [ 'kobe', 'klay', 'james' ] // 4.解構陣列的同時,給元素指定預設值,如果陣列中沒有該元素,就會使用預設值 var [name1, name2, name3, name4, name5 = 'default'] = names console.log(name1, name2, name3, name4, name5) // curry kobe klay james default
-
物件的解構:物件的解構是不按順序的,是根據key來賦值的。
const obj = { name: 'curry', age: 30, team: '金州勇士' } // 1.基本的解構 var { name, age, team } = obj console.log(name, age, team) // curry 30 金州勇士 // 2.解構的同時重新命名 var { name: newName, age: newAge, team: newTeam } = obj console.log(newName, newAge, newTeam) // curry 30 金州勇士 // 3.解構的同時指定預設值,當物件中沒有該屬性時,就會取預設值 var { height = '1.83' } = obj console.log(height) // 1.83 // 4.同時重新命名和指定預設值 var { height: newHeight = '1.83' } = obj console.log(newHeight) // 1.83
-
解構的應用場景:一般開發中拿到一個變數,可以對齊進行解構使用,比如對函式的引數進行解構。
function fn({ name, age, team }) { console.log(name, age, team) } fn(obj) // curry 30 金州勇士
1.3.let和const的使用
在ES5中,宣告變數都是使用var,從ES6開始新增了兩個宣告變數的關鍵字let和const。
-
let關鍵字:從直觀的角度來說,let和var是沒有太大的區別的,都是用於宣告一個變數。
let message = 'hello world'
-
const關鍵字:const宣告的變數稱為常量,表示儲存的資料一旦被賦值就不能再被修改了,但是如果是引用型別的資料,還是可以通過引用找到對應的物件進行修改的。
const obj = { name: 'curry', age: 30 } const names = ['curry', 'kobe'] obj.name = 'kobe' names.push('klay') console.log(obj) // { name: 'kobe', age: 30 } console.log(names) // [ 'curry', 'kobe', 'klay' ] // 注意:不能直接給引用型別重新賦值 /* obj = {} // TypeError: Assignment to constant variable. names = [] // TypeError: Assignment to constant variable. */
-
注意:與var不同的是,let和const是不允許重複宣告變數的。並且使用let和const宣告的變數是具有自己的塊級作用域的。var和let可以不設定初始值,const必須設定。
有關作用域提升的問題:
使用過var關鍵字的人都知道,var宣告的變數是會進行作用域提升的,如果使用let和const宣告的變數,是不允許在宣告之前對其進行訪問的,會直接報錯。
console.log(message) // undefined
var message = 'hello world'
console.log(message) // ReferenceError: Cannot access 'message' before initialization
let message = 'hello world'
為什麼var宣告的變數有作用域提升,而let和const沒有呢?
- 作用域提升:在宣告變數的作用域中,如果這個變數可以在宣告之前被訪問,那麼就可以稱之為作用域提升。
- 在JavaScript執行之前,會先對我們宣告的變數進行收集建立,普通變數的預設值都為undefined,只有等到執行賦值程式碼時,該變數才會被真正賦值,所以在宣告之前訪問就為undefined。
- 那let和const不能訪問,是不是let和const宣告的變數沒有在程式碼執行前被收集建立呢?到底let和const有沒有作用域提升?
- 其實let和const宣告的變數在程式碼執行前是有被收集建立的,只是不能訪問而已,所以就不能稱之為作用域提升,在ECMA262對let和const的描述中,這些變數會被建立在包含它們的詞法環境被例項化時,但是是不可以訪問它們的,直到詞法繫結被求值(也就是變數被賦值之後);
- 在使用var宣告的變數,是會新增到window上的,而let和const宣告的變數是不會被新增到window上的,實際上是被新增到了變數環境中進行儲存;
- 暫時性死區:在程式碼中,使用let、const宣告的變數,在宣告之前變數是不可以被訪問的,這種現象稱之為temporal dead zone(TDZ);
總結:在上面的程式碼中,其實message在程式碼執行前就已經被建立了,在用let進行修飾後,js底層會對其進行約束,以至於在宣告前不可以訪問。
1.4.模板字串
ES6允許使用字串模板來嵌入JS的變數或者表示式來進行拼接。
使用``來編寫字串,可以${expression}來嵌入動態內容,裡面可以寫一個表示式。
const name = 'curry'
const age = 30
console.log(`My name is ${name}\nMy age is ${age}`)
標籤模板字串:模板字串另外一種用法,可以用來呼叫函式,如果使用標籤模板字串,並且在呼叫的時候插入其他的變數。
- 模板字串被拆分了;
- 第一個元素是陣列,元素為被
${}
的字串組合; - 後面的元素是一個個
${}
中傳入的變數值;
function fn(x, y, z) {
console.log(x, y, z)
}
const name = 'curry'
const age = 30
fn`sojfo${name}hghaooa${age}jgoajg` // [ 'sojfo', 'hghaooa', 'jgoajg' ] curry 30
應用:在react中編寫css就有這麼一個庫叫styled-components
,其原理就是使用的標籤模組字串。
1.5.函式的預設引數
在ES6之前,我們編寫的函式引數是沒有預設值的,而ES6給我們提供了預設引數,如果傳入了該引數就使用傳入的值,沒有傳入就使用預設值。
-
在ES6之前,如果想實現預設引數的效果,就必須編寫以下最終程式碼。
function fn() { var m = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'aaa' var n = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'bbb' console.log(m, n) } fn() // aaa bbb fn(111, 222) // 111 222
-
在ES6中,可以在宣告函式時給其引數預設值。
-
一般預設值寫法:
function fn(m = 'aaa', n = 'bbb') { console.log(m, n) }
-
預設值搭配解構的使用:
// 1.寫法一:物件引數+解構 function fn1({ name, age } = { name: 'curry', age: 30 }) { console.log(name, age) } fn1() // curry 30 // 2.寫法二:解構+預設值 function fn2({ name = 'curry', age = 30 } = {}) { console.log(name, age) } fn2() // curry 30
-
注意:有預設值的引數一般會放到最後,在JS中函式都會有一個length屬性,length長度代表函式引數個數,但是有預設值的引數是不會被計算在內的。但是如果將有預設值的引數放在中間,那麼後面的引數也不會被計算在內的。
function fn1(x, y, z) {}
console.log(fn1.length) // 3
function fn2(x, y, z = 30) {}
console.log(fn2.length) // 2
function fn3(x, y = 30, z) {}
console.log(fn3.length) // 1
1.6.函式的剩餘引數
ES6中引用了剩餘引數,可以將不定數量的引數放入到一個陣列中,如果最後一個引數是以...為字首的,那麼它會將剩餘的引數放入到該引數中,並且作為一個陣列。
剩餘引數和arguments有什麼區別呢?
- 剩餘引數只包含那些沒有對應形參的實參,而arguments物件包含了傳給函式的所有實參;
- arguments物件不是一個真正的陣列,而rest是一個真正的陣列,可以進行陣列的所有操作;
- arguments是早期的ECMAScript中為了方便去獲取所有的引數提供的一個資料結構,而剩餘引數是ES6中提供並且希望代替arguments的;
function foo(m, n, ...args) {
console.log(m, n, args)
console.log(arguments) // [Arguments] { '0': 10, '1': 20, '2': 30, '3': 40, '4': 50 }
}
foo(10, 20, 30, 40, 50) // 10 20 [ 30, 40, 50 ]
1.7.箭頭函式
相比於普通函式,箭頭函式有一下幾個特殊點:
- 箭頭函式內部是不繫結this的,會去它上一層作用域中找;
- 箭頭函式是沒有顯示原型的,所以不能作為建構函式,使用new操作符來呼叫;
- 箭頭函式沒有自己的argments;
const fn = () => {
console.log(this)
// console.log(argments) // ReferenceError: argments is not defined
}
fn() // window物件
console.log(fn.prototype) // undefined
1.8.展開語法
展開語法(spread syntax)可以在函式呼叫或陣列構造是,將陣列表示或者字串在語法層面展開,還可以在構造字面量物件時,將物件表示式按鍵值對的方式展開。
展開語法的主要使用場景:在函式呼叫時和陣列構造時使用:
const nums = [1, 2, 3]
const str = 'abc'
function fn(x, y, z) {
console.log(x, y, z)
}
// 函式呼叫時
fn(...nums) // 1 2 3
fn(...str) // a b c
// 陣列構造時
const newNums1 = [...nums, 4, 5]
const newNums2 = [...nums, ...str]
console.log(newNums1) // [ 1, 2, 3, 4, 5 ]
console.log(newNums2) // [ 1, 2, 3, 'a', 'b', 'c' ]
1.9.數值的表示
ES6中規範了二進位制和八進位制的寫法。
const num1 = 188 // 十進位制
const num2 = 0b101 // 二進位制
const num3 = 0o567 // 八進位制
const num4 = 0x8cf // 十六進位制
// 列印轉成對應的十進位制數
console.log(num1, num2, num3, num4) // 188 5 375 2255
1.10.Symbol的使用
在ES6之前,物件屬性名都是字串形式,很容易造成屬性名衝突,Symbol是ES6中新增的一個基本資料型別,可用來生成一個獨一無二的值。
-
作為屬性名:
const s1 = Symbol() const s2 = Symbol() const s3 = Symbol() // 對比呼叫Symbol生成的值 console.log(s1 === s2) // false // Symbol值作為物件key // 寫法一:定義物件字面量時使用 const obj = { [s1]: 'aaaa' } // 寫法二:物件新增屬性 obj[s2] = 'bbbb' // 寫法三:通過Object.defineProperty() Object.defineProperty(obj, s3, { enumerable: true, configurable: true, writable: true, value: 'cccc' }) console.log(obj) // { [Symbol()]: 'aaaa', [Symbol()]: 'bbbb', [Symbol()]: 'cccc' }
-
作為屬性值:
const s1 = Symbol() const obj = { name: 'curry', age: s1 } console.log(obj) // { name: 'curry', age: Symbol() }
-
注意:Symbol作為屬性名時,不能通過
.
方式來獲取,也就是obj.s1
,只能通過obj[s1]
的方式獲取。並且在通過Object.keys()
獲取物件所有的key時,是獲取不到這些Symbol的,可以藉助Object.getOwnPropertySymbols()
來獲取所有型別為Symbol的key。console.log(obj.s1) // undefined console.log(obj[s1]) // aaaa console.log(obj[s2]) // bbbb // 使用Object.keys獲取物件所有key並列印,發現陣列中是沒有Symbol的key console.log(Object.keys(obj)) // [] // 使用Object.getOwnPropertySymbols(obj)獲取所有的Symbol的key console.log(Object.getOwnPropertySymbols(obj)) // [ Symbol(), Symbol(), Symbol() ]
延伸:Symbol的作用就是給我們建立一個獨一無二的值,如果我們想建立一個相同的Symbol可以怎麼做呢?
- 可以使用
Symbol.for()
來做到這一點; - 並且可以通過
Symbol.keyFor()
方法來獲取對應的key;
// 通過Symbol.for(描述)建立並傳入相同描述
const s1 = Symbol.for('aaaa')
const s2 = Symbol.for('aaaa')
console.log(s1 === s2) // true
// 拿到s1傳入的值
const key = Symbol.keyFor(s1)
console.log(key) // aaaa
// 將拿到的值又傳給s3
const s3 = Symbol.for(key)
console.log(s3 === s1) // true
console.log(s3 === s2) // true
1.11.Set和WeakSet的使用
在ES6之前,儲存資料的結構主要有兩種,分別是陣列和物件。在ES6中新增了另外兩種資料結構:Set、Map,以及它們另外形式的WeakSet、WeakMap。下面就先來看看Set和WeakSet。
(1)Set:用於儲存資料,類似於陣列,與陣列不同的是Set中的元素是不能重複的。
-
Set的基本使用:
// 1.建立一個Set結構 const set = new Set() // 2.往set中新增元素 set.add(30) set.add(30) // 相同的基本資料型別只會保留一個 set.add('abc') set.add(undefined) set.add({}) set.add({}) // 物件可以重複的原因是,物件存放的是記憶體地址 set.add(NaN) set.add(NaN) // 為什麼NaN也不能重複,因為在Set內部會視NaN為同一個東西 console.log(set) // Set(6) { 30, 'abc', undefined, {}, {}, NaN }
-
Set常見的屬性:
-
size:返回Set中元素的個數;
console.log(set.size) // 6
-
-
Set常見的方法:
- add(value):新增一個元素,返回set物件本身;
- delete(value):從set中刪除與value值相等的元素,返回boolean型別;
- has(value):判斷set中是否存在value這個元素,返回boolean;
- clear():清空set中所有的元素,無返回值;
- forEach(callback,[thisArg]):遍歷set物件(Set的例項化物件是一個可迭代物件,所以也支援for...of遍歷);
// add console.log(set.add(30)) // Set(1) { 30 } console.log(set.add(60)) // Set(2) { 30, 60 } console.log(set.add(90)) // Set(2) { 30, 60, 90 } // delete console.log(set.delete(40)) // 未找到40,返回false console.log(set.delete(30)) // true // has console.log(set.has(60)) // true console.log(set.has(30)) // false // forEach set.forEach(item => { console.log(item) // 60 90 }) // for...of for (const item of set) { console.log(item) // 60 90 } // clear set.clear() console.log(set) // Set(0) {}
(2)WeakSet:也要求內部元素不能重複。
-
WeakSet與Set的區別:
- 區別一:WeakSet中只能存放物件型別,不能存放基本資料型別;
- 區別二:WeakSet對元素的引用是弱引用,如果沒有其他引用對該元素進行引用,是可以被GC回收的;
- 區別三:WeakSet由於是弱引用的原因,所以是不能進行遍歷的,儲存到WeakSet中的物件是沒辦法通過遍歷獲取的;
- 什麼是弱引用?:簡單理解就是對物件型別的引用是可以看成沒有的,但是卻可以訪問其物件內部的資料;
-
WeakSet常見方法:add(value)、delete(value)、has(value),沒有clear和forEach;
-
WeakSet的基本使用:
const wSet = new WeakSet() // 只能存放物件型別 // wSet.add(10) // TypeError: Invalid value used in weak set wSet.add({ name: 'curry', age: 30 }) wSet.add([1, 2, 3])
1.12.Map和WeakMap的使用
下面來講講Map和WeakMap,主要用於儲存對映關係。
(1)Map:對於普通物件儲存對映關係,只能使用字串或者Symbol作為屬性名,如果普通物件使用物件來作為key的話,會自動將物件轉成字串,但是Map允許我們使用物件作為key。
const obj1 = { name: 'curry', age: 30 }
const obj = {
[obj1]: 'aaaa'
}
// 普通物件使用物件作為key,物件會自動轉成字串[object Object]
console.log(obj) // { '[object Object]': 'aaaa' }
-
Map的基本使用:
const obj1 = { name: 'curry', age: 30 } const obj2 = { name: 'kobe', age: 24 } // 方式一:建立map後,通過set新增屬性 const map1 = new Map() map1.set(obj1, 'aaaa') map1.set(obj2, 'bbbb') // 方式二:建立map時,傳入一個陣列 const map2 = new Map([ [obj1, 'aaaa'], [obj2, 'bbbb'] ]) console.log(map1) console.log(map2)
-
Map常見的屬性:
-
size:返回Map中元素的個數;
console.log(map1.size) // 2
-
-
Map常見的方法:與Set很相似,設定和獲取屬性不太一樣。
- set(key, value):在Map中新增key和value,並且返回整個map物件;
- get(key):根據key獲取map中的value;
- has(key):判斷map中是否包含某一個key,返回boolean值;
- delete(key):根據key刪除一個鍵值對,返回boolean值;
- clear():清空所有的屬性;
- forEach(callback, [thisArg]):遍歷map(也可使用for...of遍歷);
const map = new Map() const obj = { name: 'curry', age: 30 } // set map.set(obj, 'aaaa') map.set('bbbb', 1234) map.set(11, 'cccc') // get console.log(map.get(obj)) // aaaa // delete console.log(map.delete(11)) // true console.log(map.delete(22)) // false // has console.log(map.has('bbbb')) // true console.log(map.has('abc')) // false // forEach map.forEach((value, key) => { console.log(key, value) /* { name: 'curry', age: 30 } aaaa bbbb 1234 */ }) // clear map.clear() console.log(map) // Map(0) {}
如果map使用for...of遍歷,看一下遍歷出來的值是什麼樣子的:
// 這裡取出的item是一個個陣列,陣列第一個元素是key,第二個元素是value for (const item of map) { console.log(item) } // 所以可以在取值的時候對item進行解構 for (const [key, value] of map) { console.log(key, value) }
(2)WeakMap:也是以鍵值對的形式存在。
- WeakMap和Map的區別:與Set和WeakSet的區別類似;
- 區別一:WeakMap的key只能使用物件,不接受其他型別作為key;
- 區別二:WeakMap的key對物件的引用是弱引用,如果沒有其他引用指向這個物件,那麼GC可以回收該物件;
- 區別三:WeakMap和WeakSet一樣也不能進行遍歷;
- WeakMap常見的方法:set(key, value)、get(key)、has(key)、delete(key),沒有clear和forEach;
2.ES7相關知識點
2.1.陣列的includes方法
在ES7之前,判斷陣列中包含某一個元素,一般可以用indexOf判斷是否為-1,ES7給我們提供了includes方法來判斷陣列中是否包含指定元素,返回boolean型別;
arr.includes(valueToFind[, fromIndex])
- valueToFind:需要查詢的元素;
- fromIndex:從指定索引開始查詢,如果from為負值,就從
array.length + fromIndex
的位置開始查詢(預設值為0);
const names = ['curry', 'kobe', NaN]
console.log(names.includes('curry')) // true
console.log(names.includes('klay')) // false
console.log(names.includes('curry', 1)) // false
console.log(names.indexOf(NaN)) // -1
console.log(names.includes(NaN)) // true
注意:includes方法是可以判斷NaN是否存在的,因為includes的內部實現對NaN採用isNaN方法進行判斷。
2.2.指數運算子
在ES7之前,計算數字的指數需要通過Math.pow方法來完成,在ES7中,增加了
**
運算子,可以來計算指數。
計算2的3次方:
const res1 = Math.pow(2, 3)
const res2 = 2 ** 3
console.log(res1, res2) // 8 8
3.ES8相關知識點
3.1.獲取物件所有的value
我們知道可以使用Object.keys獲取一個物件的所有key,ES8給我們提供了Object.values來獲取所有的value值。
const obj = {
name: 'curry',
age: 30,
team: '勇士'
}
// 傳入一個物件
console.log(Object.values(obj)) // [ 'curry', 30, '勇士' ]
// 傳入一個字串,達到split的效果
console.log(Object.values('abc')) // [ 'a', 'b', 'c' ]
3.2.Object的entries方法
通過Object.entries()可以獲取到一個二維陣列,陣列中會存放可列舉的鍵值對陣列,陣列元素分別為物件的key和value。
const obj = {
name: 'curry',
age: 30,
team: '勇士'
}
// 傳入一個物件
console.log(Object.entries(obj)) // [ [ 'name', 'curry' ], [ 'age', 30 ], [ 'team', '勇士' ] ]
for (const [key, value] of Object.entries(obj)) {
console.log(key, value)
/*
name curry
age 30
team 勇士
*/
}
// 傳入一個陣列,下標作為第一個元素
console.log(Object.entries(['curry', 'kobe', 'klay'])) // [ [ '0', 'curry' ], [ '1', 'kobe' ], [ '2', 'klay' ] ]
// 傳入一個字串,下標作為第一個元素
console.log(Object.entries('abc')) // [ [ '0', 'a' ], [ '1', 'b' ], [ '2', 'c' ] ]
3.3.字串的填充
如果需要對字串進行前後填充,來實現某種展示格式,ES8中提供了padStart和padEnd方法,分別可以對字串進行首位填充。
const str = 'ssssssssss'
// 如果指定填充的位數小於等於str的長度,就不會進行填充
// 指定的位數需大於str的長度,就會填充str長度減去指定位數
// 如下指定了15,最終填充的只有5位,因為str的長度為10
console.log(str.padStart(15, '*')) // *****ssssssssss
console.log(str.padEnd(15, '-')) // ssssssssss-----
應用場景:字串的填充可以用於對身份證、銀行卡進行位數隱藏。
const bankCard = '2034399002125581'
// 擷取銀行卡後四位陣列
const lastFourNum = bankCard.slice(-4)
// 將前面陣列全部填充為*
const finalBankCard = lastFourNum.padStart(bankCard.length, '*')
console.log(finalBankCard) // ************5581
3.4.Object的getOwnPropertyDescriptors方法
Object.getOwnPropertyDescriptors()方法用來獲取一個物件的所有自身屬性的描述符。
const obj = {}
Object.defineProperties(obj, {
name: {
configurable: false,
writable: false,
enumerable: false,
value: 'curry'
},
age: {
configurable: true,
writable: true,
enumerable: true,
value: 30
}
})
console.log(Object.getOwnPropertyDescriptors(obj))
4.ES9相關知識點
4.1.物件的展開語法
在前面講了,陣列是可以使用展開語法的,在ES9中,在構建物件字面量時,也可以使用展開語法了。
const obj1 = {
name: 'curry',
age: 30
}
const newObj1 = { ...obj1, team: '勇士' }
console.log(newObj1) // { name: 'curry', age: 30, team: '勇士' }
注意:物件的展開運算子只能實現淺拷貝,如果物件內部還包含引用型別的值,就會指向同一地址空間。
const obj = {
name: 'curry',
age: 30,
friends: ['kobe', 'klay']
}
const newObj = { ...obj }
newObj.friends.push('james')
console.log(newObj) // { name: 'curry', age: 30, team: '勇士' }
console.log(obj) // { name: 'curry', age: 30, friends: [ 'kobe', 'klay', 'james' ] }
5.ES10相關知識點
5.1.flat和flatMap
(1)flat方法
按照一個可指定的深度遞迴遍歷陣列,並將所有的元素和遍歷到的子陣列合併為一個新陣列返回。(可用於陣列扁平化)
const nums = [1, 2, [3, 3], [4, [5, 5]]]
// 對陣列進行降維處理
console.log(nums.flat(1)) // [ 1, 2, 3, 3, 4, [ 5, 5 ] ]
console.log(nums.flat(2)) // [ 1, 2, 3, 3, 4, 5, 5 ]
// 如果不知道陣列巢狀了多少層,可直接指定Infinity
console.log(nums.flat(Infinity)) // [ 1, 2, 3, 3, 4, 5, 5 ]
(2)flatMap方法
首先使用對映函式對映每個元素,然後將結果壓縮成一個新陣列。
- flatMap先進行map操作,然後做flat操作;
- flatMap中的flat操作深度為1;
const arr = [[1, 2, 3, 4], ['a', 'b','c', 'd']]
// 陣列自動降一維
const newArr = arr.flatMap(item => {
return item
})
console.log(newArr) // [ 1, 2, 3, 4, 'a', 'b', 'c', 'd' ]
5.2.Object的fromEntries方法
在前面講了可以通過Object.entries()將一個物件轉換成鍵值對陣列,而Object.fromEntries()可以將鍵值對列表轉換成物件。
const entries = [
['name', 'curry'],
['age', 30]
]
const obj = Object.fromEntries(entries)
console.log(obj) // { name: 'curry', age: 30 }
應用場景:解析url的query部分,將其轉成一個物件。
const url = 'http://127.0.0.1:8000/search?name=curry&age=30'
const queryStr = url.slice(url.indexOf('?') + 1, url.length)
console.log(queryStr) // name=curry&age=30
// URLSearchParams:可用於處理url的查詢字串
const queryParams = new URLSearchParams(queryStr)
console.log(queryParams) // URLSearchParams { 'name' => 'curry', 'age' => '30' }
const paramObj = Object.fromEntries(queryParams)
console.log(paramObj) // { name: 'curry', age: '30' }
5.3.去除字串首尾空格
去除一個字串首尾空格可以通過trim()方法,而trimStart和trimEnd方法分別可以單獨去除字串前或後的空格。
// 這裡以·代表空格
const str = '····ssss···'
console.log(str.trim()) // ssss
console.log(str.trimStart()) // ssss···
console.log(str.trimEnd()) // ····ssss
5.4.Symbol的description
在前面講了Symbol型別,在建立一個Symbol型別的值時,還可以傳入指定的描述符。
const s1 = Symbol('aaaa')
const s2 = Symbol('bbbb')
console.log(s1.description) // aaaa
console.log(s2.description) // bbbb
6.ES11相關知識點
6.1.BigInt型別
在JavaScript中,如果數字過大,可能是不正確的,先看看JavaScript提供給我們的最大值和最小值是多少。
const maxNum = Number.MAX_SAFE_INTEGER
const minNum = Number.MIN_SAFE_INTEGER
console.log(maxNum) // 9007199254740991
console.log(minNum) // -9007199254740991
如果給最大值再加大數值,很明顯數值是不正確的:
console.log(maxNum + 1) // 9007199254740992
console.log(maxNum + 2) // 9007199254740992
所以,ES11引入了新的資料型別BigInt,可用於表示很大的整數:
- BigInt的寫法需要在數值後面加上
n
; - 只有數值同為BigInt型別才能進行運算(不能進行隱式轉換),如果需要運算的資料不是BigInt型別就要加上
n
轉成BigInt; - 也可使用
BigInt()
方法進行轉型別;
const bigInt = 9007199254740991n
console.log(bigInt + 2n) // 9007199254740993n
console.log(bigInt + bigInt) // 18014398509481982n
6.2.空值合併操作符(??)
如果
||
不能對空字串和0進行很好的判斷,就可以使用??
。
const test1 = ''
const test2 = 0
const test3 = null
const test4 = undefined
const res1 = test1 || 'default'
const res2 = test2 || 'default'
console.log(res1) // default
console.log(res2) // default
// ??認為空字串和0是真值的
const res3 = test1 ?? 'default'
const res4 = test2 ?? 'default'
console.log(res3) // ''
console.log(res4) // 0
// 只有當??前面的值為null或者undefined,才會使用後面的值
const res5 = test3 ?? 'default'
const res6 = test4 ?? 'default'
console.log(res5) // default
console.log(res6) // default
6.3.可選鏈
可選鏈(Optional Chaining)的主要作用就是讓程式碼在進行null和undefined判斷時更加清晰和簡潔,確保我們訪問物件屬性是安全的(因為從undefined裡面取值是會報錯的)。
const obj = {
name: 'curry',
friend: {
name: 'kobe',
age: 24
}
}
// 先判斷obj中是否有friend屬性,然後再判斷friend中是否有name屬性
console.log(obj?.friend?.name) // kobe
// console.log(obj.teammate.name) // TypeError: Cannot read property 'name' of undefined
// obj中沒有teammate屬性,所以就直接返回undefined,不會再從undefined中取name了,不會報錯影響後面程式執行
console.log(obj?.teammate?.name) // undefined
6.4.globalThis
在ES11之前獲取JavaScript的全域性物件在不同的環境下的獲取方式不同:
- 瀏覽器中:this、window獲取;
- node中:global獲取;
ES11中,對我們獲取全域性物件進行統一規範:
console.log(globalThis)
瀏覽器中:
node中:
7.ES12相關知識點
7.1.FinalizationRegistry
FinalizationRegistry物件可以讓你在物件被垃圾回收時請求一個回撥。
- FinalizationRegistry提供了這樣的一種方法:當一個在登錄檔中註冊的物件被回收時,請求在某個時間點上呼叫一個清理回撥;(清理回撥有時被稱為 finalizer )
- FinalizationRegistry可例項化一個物件,可以藉助該物件註冊想要清理回撥的物件,傳入該物件和所含的值;
let obj1 = { name: 'curry', age: 30 }
let obj2 = { name: 'kobe', age: 24 }
// 例項化登錄檔
const register = new FinalizationRegistry(value => {
console.log(`${value}物件被銷燬了`)
})
// 註冊obj1和obj2,並指定其值
register.register(obj1, 'obj1')
register.register(obj2, 'obj2')
// 將兩個物件銷燬
obj1 = null
obj2 = null
需要藉助瀏覽器的GC來進行測試,當兩個物件被真正回收了,就會呼叫清理回撥,列印對應內容:
7.2.WeakRef
在前面講了WeakSet和WeakMap中的元素對物件的引用都是弱引用,WeakRef就可以專門實現對一個物件進行弱引用,也就是說該物件被GC回收時,是不會看它是否有弱引用的,有沒有弱引用一樣被回收。
可以結合上面的FinalizationRegistry進行測試:
- WeakRef建立的弱引用需通過
deref()
來訪問物件屬性;
let obj = { name: 'curry', age: 30 }
// 建立一個弱引用weakObj指向物件{ name: 'curry', age: 30 }
let weakObj = new WeakRef(obj)
// 拿到{ name: 'curry', age: 30 }物件
// console.log(weakObj.deref())
// console.log(weakObj.deref().name) // curry
// console.log(weakObj.deref().age) // 30
const register = new FinalizationRegistry(value => {
console.log(`${value}物件被銷燬了`)
})
register.register(obj, 'obj')
// 去掉obj對物件{ name: 'curry', age: 30 }的引用
obj = null
// 等物件回收後再列印
setTimeout(() => {
// 注意:當弱引用的物件被GC回收後,weakObj.deref()的值為undefined
// 如果不想報錯,可以使用可選鏈或者邏輯與
console.log(weakObj.deref()?.name)
console.log(weakObj.deref() && weakObj.deref().name)
}, 10000)
7.3.邏輯賦值運算
ES12為我們提供了三種邏輯賦值運算:
-
||=
:邏輯或賦值運算;let msg = undefined msg ||= 'default' console.log(msg) // default /* 等同於: msg = msg || 'default' */
-
&&=
:邏輯與賦值運算;let obj = { name: 'curry', age: 30 } // 在obj有值的情況下,將obj賦值為obj.name obj &&= obj.name console.log(obj) // curry /* 等同於: obj = obj && obj.name */
-
??=
:邏輯空賦值運算;let msg = 0 // 當msg為null或undefined情況下,將msg賦值為default,這裡為0,所以值還是0 msg ??= 'default' console.log(msg) // 0 /* 等同於: msg = msg ?? 'default' */
注意:該新增特性可能過新,可能出現node版本不支援的情況,可以在瀏覽器中列印檢視。
7.4.數值分隔符
當數值過長時,ES12允許我們使用
_
進行連線,方便閱讀。
const num = 100_000_000
console.log(num) //100000000
7.5.字串替換
ES12提供了一個replaceAll方法,該返回一個新字串,新字串所有滿足
pattern
的部分都已被replacement
替換。pattern
可以是一個字串或一個RegExp,replacement
可以是一個字串或一個在每次匹配被呼叫的函式。
const newStr = str.replaceAll(regexp|substr, newSubstr|function)
將所有的a替換為b:
const str = 'ababababab'
// 字串
const newStr1 = str.replaceAll('a', 'b')
// 正則
const regex = /a/g
const newStr2 = str.replace(regex, 'b')
console.log(newStr1) // bbbbbbbbbb
console.log(newStr2) // bbbbbbbbbb
注意:該新增特性可能過新,可能出現node版本不支援的情況,可以在瀏覽器中列印檢視。
總結:
本篇文字只總結了ES6-ES12部分簡單知識點,還有一些其它ES6之後新增的特性,如Promise、迭代器、生成器、async、await等等,將會在後續的文章中進行整理。