ES11屢試不爽的新特性,你用上了幾個?

ES11規範於今年的年初完成,引入了許多新標準,本文講着重研究下其中幾個實用且有趣的新標準

特性搶先知:

  • 私有變量
  • Promise.allSettled
  • BigInt 全新的數據類型
  • Nullish Coalescing Operator 空位合併運算符
  • Optional Chaining Operator 可選鏈運算符
  • Dynamic Import 動態導入
  • String.prototype.matchAll 新增matchAll
  • globalThis 新增全局對象
  • Module Namespace Exports 導入特定命名空間

私有變量

嚴格限制一些用於內部使用的Class變量,只須要在變量前添加#,就可使其成爲私有變量,而且沒法在class外部直接訪問javascript

下面咱們以一個簡單的前端

class Hero {
    #aggressivity = 0
    constructor (aggressivity){
      this.#aggressivity = aggressivity
    }
    getHurt(){
      return this.#aggressivity
    }
    setAggressivity(aggressivity){
      this.#aggressivity = aggressivity
    }
}

const shooter = new Hero(100)
let hurt = shooter.getHurt()
console.log(hurt) //100
console.log(shooter.#aggressivity) //Error : Uncaught SyntaxError: Private field '#aggressivity' must be declared in an enclosing class

上面代碼咱們會發現,沒法直接進行訪問#aggressivity,將拋出異常
只能經過class裏進行訪問,可經過統一class的公共方法進行統一修改java

假設目前射手攜帶了狂暴技能,提高了10%傷害,咱們能夠經過setAggressivity來修改攻擊力u=2761826140,1511597816&fm=26&gp=0.jpeg正則表達式

let aggressivity = parseInt(hurt * 1.1)
shooter.setAggressivity(aggressivity)
console.log(shooter.getHurt()) // 110

Promise.allSettled

談及這個新特性以前,咱們先簡單回顧下Promise.all以及Promise.race,推測下爲何須要Promise.allSettled這個新特性編程

Promise.all:能夠將多個Promise實例包裝成一個新的Promise實例。同時,成功和失敗的返回值是不一樣的,成功的時候返回的是一個結果數組,而失敗的時候則返回最早被reject失敗狀態的值數組

let p1 = new Promise((resolve, reject) => {
  resolve('成功了')
})

let p2 = new Promise((resolve, reject) => {
  resolve('success')
})

let p3 = Promse.reject('失敗')

Promise.all([p1, p2]).then((result) => {
  console.log(result) //['成功了', 'success']
}).catch((error) => {
  console.log(error)
})

Promise.all([p1,p3,p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error) // 失敗了,打出 '失敗'
})

Promise.race:返回一個promise,一旦某個Promise觸發resolve或者reject,就直接返回該promise結果狀態promise

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then((value) => {
  console.log(value);
});
//輸出 "two" 由於promise2返回結果比promise1快

有時候咱們可能須要知道全部的結果作一些操做,並不關心其執行結果是否成功,在沒有Promise.allSettled以前,咱們須要本身實現,可經過以下實現Promise.allSettled瀏覽器

let allSettled = (funcArr) => {
  return new Promise((resolve) => {
    let sttled = 0
    let result = []
    for(let index = 0;index<funcArr.length;index++){
      const element = funcArr[index]
      element
      .then(res => { 
        result[index] = {
          status: 'fulfilled',
          value: res
        }
      })
      .catch(err => { 
        result[index] = {
          status: 'rejected',
          reason: err
        }
      })
      .finally(() => { ++sttled === funcArr.length && resolve(result) })
    }
  })
}

//使用
const promises = [
    Promise.reject('c'),
    Promise.resolve('a'),
    Promise.resolve('b'),
];

allSettled(promises).then(res => {
    console.log(res)
})

// 打印結果
// [{"status":"rejected","reason":"c"},
// {"status":"fulfilled","value":"a"},
// {"status":"fulfilled","value":"b"}]

而Promise.allSettled新特性出來後,咱們能夠直接使用而不須要單獨去實現了安全

const promises = [
    Promise.reject('c'),
    Promise.resolve('a'),
    Promise.resolve('b'),
];
Promise.allSettled(promises).then(res =>{
    console.log(res)
})
// 打印結果
// [{"status":"rejected","reason":"c"},
// {"status":"fulfilled","value":"a"},
// {"status":"fulfilled","value":"b"}]

返回結果裏會將返回一個數組,包含了全部成功與失敗的結果,數組每項爲對象,均含有status屬性,對應fulfilled和rejected。
當狀態爲fulfilled時,表明着成功,包含一個value,表明着成功的結果
當狀態爲rejected時,表明着失敗,包含一個reason,表明着失敗的緣由async

BigInt

JS中缺乏顯式整數類型經常使人困惑。許多編程語言支持多種數字類型,如浮點型、雙精度型、整數型和雙精度型,但JS卻不是這樣。在JS中,按照IEEE 754-2008標準的定義,全部數字都以雙精度64位浮點格式表示。

在此標準下,沒法精確表示的很是大的整數將自動四捨五入。確切地說,JS 中的Number類型只能安全地表示-9007199254740991 (-(2^53-1)) 和9007199254740991(2^53-1)之間的整數,任何超出此範圍的整數值均可能失去精度。

console.log(9999999999999999);    //10000000000000000

JS 提供Number.MAX_SAFE_INTEGER常量來表示 最大安全整數,Number.MIN_SAFE_INTEGER常量表示最小安全整數:

// 注意最後一位的數字
const A = Number.MAX_SAFE_INTEGER + 1
const B = Number.MAX_SAFE_INTEGER + 2

console.log(Number.MAX_SAFE_INTEGER) //9007199254740991
console.log(A) //9007199254740992
console.log(B) //9007199254740992

console.log(A === B) //true

當數據超出範圍就會失去精度,達不到咱們預期的結果。

BigInt橫空出世,能夠在標準JS中執行對大整數的算術運算,沒必要擔憂精度損失風險

建立BigInt數據類型的方式很是簡單,在整數後面追加n便可,或者經過BigInt()進行建立實例

const bigint = 999999999999999999n
const bigintByMethod = BigInt('999999999999999999')
console.log(bigint) //999999999999999999n
console.log(bigintByMethod) //999999999999999999n
console.log(bigint === bigintByMethod) //true

//布爾值
console.log(BigInt(true)) //1n
console.log(BigInt(false)) //0n

console.log(typeof bigint) //"bigint"

BigInt 與 Number是兩種數據類型,沒法進行運算,能夠進行大小比較

console.log(88n == 88) //true
console.log(88n === 88) //false
console.log(88n+1) //Error =>Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions

BigInt之間,除了一元加號(+)運算符外,其餘都可用於BigInt

console.log(1n + 2n) //3n
console.log(1n - 2n) //-1n
console.log(+ 1n) //Uncaught TypeError: Cannot convert a BigInt value to a number
console.log(- 1n) //-1n
console.log(10n * 20n) //200n
console.log(23n%10n) //3n
console.log(20n/10n) //2n
......

須要注意的是,除法運算符會自動向下舍入到最接近的整數

console.log(25n / 10n) //2n
console.log(29n / 10n) //2n

最後還有個注意點就是,Boolean類型和BigInt類型的轉換時,處理方式和Number類型,只要不是0n,BigInt就被視爲true

if (5n) {
    // 這裏代碼塊將被執行
}

if (0n) {
    // 這裏代碼塊不會執行
}

Nullish Coalescing Operator 空位合併運算符

新增一個邏輯運算符??,處理null和undefined,工做原理與邏輯or( || )相似,可是與此相反

||若是爲真即返回左側值,不然返回右側

0 || 5 // return 5
"" || 5 // return 5
"前端公蝦米" || 'V5' //return "前端公蝦米"

??若是爲null或者undefined,即返回右側,不然返回左側

0 ?? 5 //return 0
"" ?? 5 //return ""
null ?? 5 // return 5
undefined ?? 5 // return 5
false ?? 5 //return false
NaN ?? 5 // return NaN

在使用該??運算符時,須要注意的是

  • 不可與其餘運算符組合使用,例如&&、||
  • 但若使用括號包裹則能夠組合使用
"前端公蝦米" || undefined ?? "Sneaker" //Uncaught SyntaxError: Unexpected token '??'
"前端公蝦米" && undefined ?? "Sneaker" //Uncaught SyntaxError: Unexpected token '??'

("前端公蝦米" || undefined) ?? "公^衆^號" //"前端公蝦米"
("回覆學習領取視頻資料" && null) ?? "一塊兒學習" //"一塊兒學習"

Optional Chaining Operator 可選鏈運算符

平常開發中,很多開發者會碰到Cannot read property XXX of undefined,拋出沒法從未定義的數據中讀取某個字段

可選鏈運算符在查找嵌套對象時,找到鏈中的第一個undefined或者null後會當即終止,並返回undefined,而不會不斷向下查找而致使拋錯

const obj = {
  foo: {
    bar: {
      baz: 42,
    },
  },
}
console.log(obj.fo.bar.baz) //Uncaught TypeError: Cannot read property 'bar' of undefined

在諸如此類的對象裏,咱們一般進行數據安全檢查來訪問嵌套對象,不然將拋錯
if(obj&&obj.foo&&obj.foo.bar){
    console.log(obj.foo.bar.baz) // 42
}

在可選鏈運算符可以使用的如今,咱們只需這樣進行屬性的讀取

console.log(obj?.foo?.bar?.baz) //42
            
console.log(obj.foo.bar?.baz) //42

Dynamic Import 動態導入

在標準的import導入中,是靜態導入的,全部被導入的模塊是在加載時就被編譯的,沒法按需編譯。當咱們須要條件導入的時候,都只能使用require().

但如今,咱們有辦法改善此類狀況了,由於動態導入能夠有效的減小未使用代碼的編譯,能夠提升首屏加載速度,按需加載。
那麼,爲何咱們須要動態導入呢?

  • 靜態導入消耗加載時間,不少模塊並不是首屏須要渲染
  • 靜態導入會在導入時消耗大量內存
  • 可能會存在有些模塊在加載時不存在
  • 減小一些有條件依賴的反作用
//通用導入方式
import("/module/sneaker/test.js")
.then(module => {
    //模塊相關操做
})

//await
const getModule = await import("/module/sneaker/test.js")

//經過async await
const getUserInfo = async (hasLogin) => {
    if(!hasLogin){
        const user = await import("/module/sneaker/user.js")
    user.getInfo()
    }
}

matchAll

基於String原型上的一個新方法,容許咱們匹配一個字符串和一個正則表達式,返回值是全部匹配結果的迭代器。
能夠經過for...of遍歷或者操做符...Array.from將結果迭代器轉換成數組

const string = 'Hello Sneaker,Where is the library?'
const regex = /[A-W]/g
const matches = string.matchAll(regex)

console.log(...matches)
//["H", index: 0, input: "Hello Sneaker,Where is the library?", groups: undefined] 
//["S", index: 6, input: "Hello Sneaker,Where is the library?", groups: undefined] 
//["W", index: 14, input: "Hello Sneaker,Where is the library?", groups: undefined]

globalThis

這是爲了提供一種訪問全局對象的標準方法。
在瀏覽器中,咱們可使用window/self/frames來訪問全局對象,但對於Web Workers只能使用self,Node中又徹底不一樣,須要使用global

globalThis成爲標準以前,獲取全局對象咱們可能須要這麼作

const globalObj = (()=>{
    if(self) return self
  if(window) return window
  if(global) return global
  throw new Error('Sorry!No global obj found')
})
//Browser 
globalThis === window //true

//Webworker
globalThis === self //true

//Node
globalThis === global //true

今後實現全局對象的大一統!

Module Namespace Exports 導入特定命名空間

export * as ns from './module

//等同於
import * as ns from './module'
export {ns}

導入特定命名空間實則並無導入模塊,只是對模塊進行轉發,致使在此模塊中不可直接使用此模塊

參考

  • ecma-262
  • MDN

最後

特性不少但有的頗有趣,好比可選鏈和空位合併運算符,屢試不爽,至於有多爽,你試了才知道。新特性日常不寫仍是容易忽略淡忘的,建議日常能夠下意識的常常回顧運用,一塊兒學習一塊兒成長。

本文首發公衆號【前端公蝦米】,若有錯誤,還望聯繫我指出【y3517320520】

相關文章
相關標籤/搜索