天天看點

ES7~ES13那些事兒

ES7~ES12都可以統稱為ES6+,ES6等價于ES2015,ES6是一個非常大的版本更疊,但是後面ES7、ES8等等增加的知識并不是很多,但是肯定也是有用的,存在即合理。是以這篇就總結一下ES6之後增加的一些常用的主要的知識

1. ES7

ES7主要介紹兩個,一個includes方法,一個指數運算符 

① includes

我們在之前,會通過indexOf來判斷一個數組中是否存在某個元素 

let list = ['a', 'b', 'c']

if(list.indexOf('b') >= 0) {
  console.log('包含----->>>')
}      

現在有了 includes 可以直接使用該方法,該方法可以讓代碼的可讀性更好

let list = ['a', 'b', 'c']

if(list.includes('b')) {
  console.log('包含----->>>')
}      

那麼有什麼差別呢?差別就在于對 NaN 的判斷

我們先使用indexOf來判斷一下 

let list = ['a', 'b', 'c', NaN]

if(list.indexOf(NaN) !== -1) {
  console.log('包含----->>>')  // 控制台并沒有列印
}      

它說我們不包含NaN,但是是存在的, 當然我們的數組中一般也很少會有 NaN,但是一旦碰到的話,可能達不到我們預期的效果。

而我們使用 includes 就可以準确的判斷

let list = ['a', 'b', 'c', NaN]

if(list.includes(NaN)) {
  console.log('包含')  // 包含
}      

② **指數運算文法 

在之前我們會通過 Math.pow() 來進行指數的運算 

let pow = Math.pow(2, 3)

console.log(pow) //8      

現在有了 ** 文法,确實是友善多了 

let pow = 2 ** 3

console.log(pow) //8      

2. ES8 

1. Object.values() 

之前是有個 Object.keys 的 ,現在有了Object.values() 其實而很簡單,很小的一個知識點,直接上代碼

const obj = {
  name: 'wft',
  age: 18
}

console.log(Object.keys(obj)) // [ 'name', 'age' ]
console.log(Object.values(obj)) // [ 'wft', 18 ]

/**
 * 很少用
 */
// 傳入數組
console.log(Object.keys([1, 2, 3])) // [ '0', '1', '2' ]
console.log(Object.values([1, 2, 3])) // [ 1, 2, 3 ]
// 傳入字元串
console.log(Object.keys('abc')) // [ '0', '1', '2' ]
console.log(Object.values('abc')) // [ 'a', 'b', 'c' ]      

2. Object.entries 

entries()方法 傳回一個給定對象自身可枚舉屬性的鍵值對數組,其排列與使用for...in 循環周遊該對象時傳回的順序一緻(差別在于for...in循環也枚舉原型鍊中的屬性)

簡單說就是Object.entries()可以把一個對象的鍵值以數組的形式周遊出來,結果和for...in一緻,但不會周遊原型屬性

const obj = {
  name: 'wft',
  age: 18
}

let objEntries = Object.entries(obj)
console.log(objEntries) // [ [ 'name', 'wft' ], [ 'age', 18 ] ]

for(let [key, val] of objEntries) {
  console.log(key, '----->>>')
  console.log(val, '----->>>')
}      

也可以傳入一個數組,大家感興趣可以自己試一下

這個在之前有寫過一次應用場景,大家可以去看下 

​​for of循環擷取index索引值,并且修改自身元素​​

3. String Padding:padStart  和  padEnd

其實用的不多,我是沒用過,要實作個什麼事,都是可以通過其他方式來實作的,就看自己對什麼方法用的熟 

兩個字元串的方法,參數一樣。分别對字元串進行首尾填充

接受兩個參數

第一個參數:傳入要填充到的長度是幾

第二個參數: 選擇用什麼填充

let message = 'Hello World'

// 将message從前面用 * 填充至長度15,然後再從後面用 - 填充至長度20
let newMessage = message.padStart(15, '*').padEnd(20, '-')

console.log(newMessage) //****Hello World-----      

4. Trailing-Commas(結尾的逗号)使用 (做個了解就好)

這個怎麼說呢,,就是在ES8之前我們函數的最後一個參數後面是不能有逗号的,是不允許的,直接會報錯,但是ES8之後,有了這麼個文法,就可以加了,就像下面的例子

function fn(m, n,) {

}

fn(1, 2,)      

這個在之前就會報錯的,但是ES8之後就可以這麼寫了。 

即便是可以這麼寫,我也不會這麼去寫的,畢竟有點兒強迫症,影響代碼美觀。那出這個文法的意義何在?就是為了可能有一些其他語言的js開發者,有那麼個習慣(在參數後面習慣性加個逗号),其實也就是這麼個事。做個了解好了 

5. Object.getOwnPropertyDescriptors() (了解一下好了)

這個是擷取所有屬性的描述符 ,了解一下就好,我是目前還沒用過呢,見到能認識就好

let obj = {
  name: 'wft',
  age: 18
}

console.log(Object.getOwnPropertyDescriptors(obj))
// {
//   name: {
//     value: 'wft',
//     writable: true,
//     enumerable: true,
//     configurable: true
//   },
//   age: { value: 18, writable: true, enumerable: true, configurable: true }
// }      

其他還有async await的使用,這個也挺常用的,也就不做贅述了 

3. ES9 

  • Async iterators :疊代器
  • Object spread operators: 這個就是 ...運算符結構對象
  • Promise finally: Promise新增了 finally

大家做個了解哈,感興趣的話可以百度一下。偷個懶。。。 

4. ES10 

① flat 

這個就是扁平數組,對數組進行降維 

let list = [1, 2, [3, 4], [5, [6, 7, 8]], 9, 10]

// 接受一個參數,降幾層,Infinity無限扁平
let target = list.flat(Infinity)

console.log(target) //[ 1, 2, 3, 4,  5, 6, 7, 8, 9, 10 ]      

之前得部落格:​​多元數組扁平化處理​​ 

② flatMap 

直接給大家上兩段代碼,對比一下(和數組的map方法,有點兒像,但也有點兒差距),最明顯的是 

let infos = [ "Hello World", "你好啊 李銀河!", "My name is WFT"]

//  map 使用
let target1 = infos.map(item => item.split(" "))  // [ [ 'Hello', 'World' ], [ '你好啊', '李銀河!' ], [ 'My', 'name', 'is', 'WFT' ] ]

// flatMap 使用
let target2 = infos.flatMap(item => item.split(" ")) // [ 'Hello', 'World', '你好啊', '李銀河!', 'My', 'name', 'is', 'WFT' ]      

flatMap : 如果裡面有數組的話,會自動降一個次元(注意是一個次元!) 

③ Object.fromEntries

前面說了 Object.entries , Object.fromEntries就是将Object.entries再轉回去

let obj = {
  name: 'wft',
  age: 18
}

let entries = Object.entries(obj) // [ [ 'name', 'wft' ], [ 'age', 18 ] ]

let fromEntries = Object.fromEntries(entries) // { name: 'wft', age: 18 }      

④ trimStart 和 trimEnd 

之前有個 trim() 方法,是去掉字元串首尾空格,現在 trimStart 就是去除前面的空格,trimEnd去除後面的空格,使用起來也很簡單

const message = "   Hellow World   "

console.log(message.trimStart())
console.log(message.trimEnd())      

5. ES11 

① 大整數 BigInt 的使用  

// ES11之前 max_safe_integer
const maxInt = Number.MAX_SAFE_INTEGER
console.log(maxInt) // 9007199254740991

// 再大的數字話 也可以表示,但是就不一定準确了,是以為了安全考慮就不要使用比這個更大的
console.log(maxInt + 1) // 9007199254740992
console.log(maxInt + 2) // 9007199254740992

// ES11之後:BigInt
const bigInt = 900719925474099100n
// 如果相加的話就必須後面加上 n , 讓其成為同一類型的資料
console.log(bigInt + 10n) // 900719925474099110n

const num = 100
console.log(bigInt + BigInt(num)) // 900719925474099200n

const smallNum = Number(bigInt)
console.log(smallNum) // 900719925474099100      

② Nullish-Coalescing-operator.js(空值合并運算符的使用)

先舉個例子, 

當我們拿到從背景傳回的資料的時候,為了更嚴格一些,經常會對傳回的資料進行判斷,例如:假設從背景拿到data這麼個對象,裡面有一些字段是展示在頁面上的,這個時候就得去判斷,傳回的資料裡面有沒有這個字段,如果沒傳回的話,我們就會給其一個預設值,就像這樣:this.value = data.value || 'default value'。使用了 ||  運算符,但是這個是有問題的。|| 運算符就是目前面為 null 、undefined、0、''的時候都會取我們後面的預設值,顯然這不是我們想要的結果,如果背景傳回0,我們肯定要0的,而不是取預設值。是以 || 運算符是存在一定隐患的。

但是在ES11之後新增了 空值合并運算符(??),我想這個才是我們想要的,?? 運算符隻判斷是否為 null 和 undefined ,如果為null或者undefined才會取我們的預設值!!

下面就用代碼看下這個兩個運算符的差別 

// undefined 的情況
let foo = undefined

let bar1 = foo || 'default value'
let bar2 = foo ?? 'default value'

console.log(bar1)  // default value
console.log(bar2)  // default value      
// null 的情況
let foo = null

let bar1 = foo || 'default value'
let bar2 = foo ?? 'default value'

console.log(bar1)  // default value
console.log(bar2)  // default value      
// 0 的情況
let foo = 0

let bar1 = foo || 'default value'
let bar2 = foo ?? 'default value'

console.log(bar1)  // default value
console.log(bar2)  // 0      
// 空字元串 的情況
let foo = ''

let bar1 = foo || 'default value'
let bar2 = foo ?? 'default value'

console.log(bar1)  // default value
console.log(bar2)  // ''(其實啥都沒有,就是個空字元串)      

 這樣看來,以後還是使用 空值運算符(??)  比較穩健呀!

③ OptionalChaining.js(可選連的使用)

其實也是個判斷的東西 

let obj = {
  name: 'wft'
}

let value = obj.info.age

console.log(value)      

大家看這段代碼,肯定是會報錯的吧,首先obj.info就是undefined,那undefined.age。。。不報錯就怪了。 我們可以修改為如下方式 

let obj = {
  name: 'wft'
}

if(obj && obj.info && obj.info.age) {
  let value = obj.info.age
  console.log(value)
}      

這樣是可以的,但确顯得尤為麻煩,程式員就是懶呀, 

從ES11之後就有了可選鍊的使用,上面就可以這麼寫了 

let obj = {
  name: 'wft'
}

let value = obj?.info?.age ?? 'default value'

console.log(value)      

看吧,既簡單又明了,這個文法真的特别棒!但是注意這個是ES11才有的文法,使用node的話,也要注意下node的版本 

 6. ES12

ES12有點兒新了,可能對浏覽器或者node等的版本會有要求,大家注意下,我現在的node版本是v14.20.0,這個版本在node環境中用不了,是以用浏覽器測試  

 ① FinalizationRegistry

FinalizationRegistry 這個類 可以監聽到某個對象被銷毀,在ES12之前是不可以的
// 執行個體化
const finalRegistry = new FinalizationRegistry(value => {
  console.log("注冊在finalRegistry的對象,某一個被銷毀", value)
})

let obj = { name: 'wft' }
let o = { name: 'lilei' }

// 調用 register方法 将想要監聽到的對象注冊進去,這樣在對象銷毀的時候,會回調上面執行個體化的時候傳入的函數
finalRegistry.register(obj, 'obj - value')
finalRegistry.register(o, 'o - value')

obj = null  // 銷毀對象 obj
o = null  // 銷毀對象 o      

看下浏覽器的列印 

ES7~ES13那些事兒

② WeakRef 

WeakSet 和 WeakMap 是基于弱引用的資料結構,​​ES2021​​ 更進一步,提供了 WeakRef 對象,用于直接建立對象的弱引用 

WeakRef 對象允許您保留對另一個對象的弱引用,而不會阻止被弱引用對象被 GC 回收 

​​WeakRef mdn​​

let obj = { name: 'wft' }

let info = new WeakRef(obj)      

如果我們預設将一個對象指派給另外一個引用,那麼這個引用是一個強引用。

上面示例中,obj是原始對象,構造函數WeakRef()建立了一個基于obj的新對象info。這裡,info就是一個 WeakRef 的執行個體,屬于對obj的弱引用,垃圾回收機制不會計入這個引用,也就是說,info的引用不會妨礙原始對象obj被垃圾回收機制清除。

WeakRef 執行個體對象有一個deref()方法,如果原始對象存在,該方法傳回原始對象;如果原始對象已經被垃圾回收機制清除,該方法傳回undefined

let obj = { name: 'wft' }

let info = new WeakRef(obj)

obj = null

let o = target.deref()

setTimeout(() => {
  console.log(o, '----->>>')  // 應該是 undefined 才對
}, 10000)      

③ 邏輯指派運算符 

 新增了三個邏輯運算,分别是 ||=、&&=、??=

其實和咱們常用的 += 一個道理,是以這裡就不再贅述了,舉個例子: 

//  ||= 邏輯或指派運算
let message = undefined
// message = message || "default value"
message ||= "default value" // 等價于上面的寫法


//  &&= 邏輯與指派運算



//  ??= 邏輯空指派運算      

7. ES13 

ES13太新了,如果使用太舊的浏覽器可能是就跑不起來,舊的浏覽器不支援的。就目前的項目來說,還是不推薦大家使用這種方式,還是用以前的方式,更保險一些。

① method.at() 

以前我們擷取數組或者字元串中的某個元素的時候,會使用中括号,然後裡面是對應的索引值,但是不支援負數。現在又新增了at方法,裡面是支援寫入負數的 

let a = [1,2,3]
console.log(a.at(1)) // 2
console.log(a.at(-1)) // 3

let str = 'Hello World'
console.log(str.at(1)) // e
console.log(str.at(-1)) // d      

② Object.hasOwn() 

判斷一個對象的屬性是否是自身含有的屬性,我們之前都是使用hasOwnProperty(),如下所示: 

function Fn(name, age) {
  this.name = name
  this.age = age
}

Fn.prototype.height = '1.88'

const obj = new Fn('wft', 18)

for(let key in obj) {
  if(obj.hasOwnProperty(key)) {
    console.log(key, obj[key], '----->>>')
  }
}      

for in 周遊會把原型上的屬性也周遊出來,我們會使用hasOwnProperty()來判斷該屬性是不是自身含有的屬性,來過濾掉非自身含有的屬性。上面的例子中 height 就不是自身含有的屬性。 

// 情況1

const obj = {
  name: 'wft',
  age: 18,
  hasOwnProperty: function() {
    return '123'
  }
}

console.log(obj.hasOwnProperty())  // 123  這個使用就相當于重寫了方法,那就判斷不出某個屬性是否是自身含有的屬性了


// 情況2

const info = Object.create(null)

console.log(info.hasOwnProperty) //undefined      
// 情況1

const obj = {
  name: 'wft',
  age: 18,
  hasOwnProperty: function() {
    return '123'
  }
}

obj.__proto__.height = '1.88'

console.log(Object.hasOwn(obj, 'age'))  // true
console.log(Object.hasOwn(obj, 'height')) // false


// 情況2

const info = Object.create(null)

console.log(Object.hasOwn) // ƒ hasOwn() { [native code] }