2022前端應(yīng)該掌握的10個(gè) JS 小技巧

你知道 0 ?? 1等于多少嗎?

現(xiàn)在前端發(fā)展很快,各種技術(shù)和框架層出不窮、百花齊放,很多人都喊學(xué)不動(dòng)啦!事實(shí)上JavaScript 作為前端的主要語言,雖然它的發(fā)展很快,每年都會(huì)出一些新特性,但視乎 JavaScript 開發(fā)者的發(fā)展速度更快一些,因?yàn)楹芏嘞鄬?duì)較新的功能都已經(jīng)有很高的采用率

下面來看看那些具有較高采用率的新特性,2022你應(yīng)該了解的10個(gè) JS 小技巧

1. 用??代替||,用于判斷運(yùn)算符左側(cè)的值為nullundefined時(shí),才返回右側(cè)的值

??運(yùn)算符是 ES2020 引入,也被稱為null判斷運(yùn)算符( Nullish coalescing operator)

它的行為類似||,但是更嚴(yán)

||運(yùn)算符是左邊是空字符串或false0falsy值,都會(huì)返回后側(cè)的值。而??必須運(yùn)算符左側(cè)的值為nullundefined時(shí),才會(huì)返回右側(cè)的值。因此0||1的結(jié)果為1,0??1的結(jié)果為0

例如

const response = {
    settings: {
        nullValue: null,
        height: 400,
        animationDuration: 0,
        headerText: '',
        showSplashScreen: false
    }
};

const undefinedValue = response.settings.undefinedValue ?? 'some other default'; // result: 'some other default'
const nullValue = response.settings.nullValue ?? 'some other default'; // result: 'some other default'
const headerText = response.settings.headerText ?? 'Hello, world!'; // result: ''
const animationDuration = response.settings.animationDuration ?? 300; // result: 0
const showSplashScreen = response.settings.showSplashScreen ?? true; // result: false

瀏覽器支持情況

微信圖片_20220616094620.jpg

2. 使用?.簡化&&和三元運(yùn)算符

?.也是ES2020 引入,有人稱為鏈判斷運(yùn)算符(optional chaining operator)

?.直接在鏈?zhǔn)秸{(diào)用的時(shí)候判斷,判斷左側(cè)的對(duì)象是否為nullundefined,如果是的,就不再往下運(yùn)算,返回undefined,如果不是,則返回右側(cè)的值

例如

var street = user.address && user.address.street;

var fooInput = myForm.querySelector('input[name=foo]')
var fooValue = fooInput ? fooInput.value : undefined

// 簡化
var street = user.address?.street
var fooValue = myForm.querySelector('input[name=foo]')?.value

注:常見寫法

  • obj?.prop 對(duì)象屬性
  • obj?.[expr] 對(duì)象屬性
  • func?.(...args) 函數(shù)或?qū)ο蠓椒ǖ恼{(diào)用

瀏覽器支持情況


21.jpg

3. 使用動(dòng)態(tài)導(dǎo)入import()實(shí)現(xiàn)按需加載(優(yōu)化靜態(tài)import)

我們可以使用 import 語句初始化的加載依賴項(xiàng)

import defaultExport from "module-name";
import * as name from "module-name";

但是靜態(tài)引入的import 語句需要依賴于 type="module"script標(biāo)簽,而且有的時(shí)候我們希望可以根據(jù)條件來按需加載模塊,比如以下場(chǎng)景:

  • 當(dāng)靜態(tài)導(dǎo)入的模塊很明顯的降低了代碼的加載速度且被使用的可能性很低,或者并不需要馬上使用它
  • 當(dāng)靜態(tài)導(dǎo)入的模塊很明顯的占用了大量系統(tǒng)內(nèi)存且被使用的可能性很低
  • 當(dāng)被導(dǎo)入的模塊,在加載時(shí)并不存在,需要異步獲取
  • 當(dāng)被導(dǎo)入的模塊有副作用,這些副作用只有在觸發(fā)了某些條件才被需要時(shí)

這個(gè)時(shí)候我們就可以使用動(dòng)態(tài)引入import(),它跟函數(shù)一樣可以用于各種地方,返回的是一個(gè) promise

基本使用如下兩種形式

//形式 1
import('/modules/my-module.js')
  .then((module) => {
    // Do something with the module.
  });
  
 //形式2
let module = await import('/modules/my-module.js');

瀏覽器支持情況


3.jpg

4. 使用頂層 await(top-level await)簡化 async 函數(shù)

其實(shí)上面的代碼就有用到

let module = await import('/modules/my-module.js')

頂層 await 允許開發(fā)者在 async 函數(shù)外部使用 await 字段

因此

//以前
(async function () {
  await Promise.resolve(console.log('??'));
  // → ??
})();

//簡化后
await Promise.resolve(console.log('??'));

瀏覽器支持情況


4.jpg

5. 使用String.prototype.replaceAll()簡化replace一次性替換所有子字符串

String.prototype.replaceAll()用法與String.prototype.replace()類似

但是replace僅替換第一次出現(xiàn)的子字符串,而replaceAll會(huì)替換所有

例如需要替換所有a為A:

// 以前
console.log('aaa'.replace(/a/g,'A')) //AAA

// 簡化后
console.log('aaa'.replaceAll('a','A')) //AAA

瀏覽器支持情況

5.jpg

6. 使用Proxy替代Object.defineProperty

為什么使用 Proxy 替代 Object.defineProperty,簡單總結(jié)Proxy的幾點(diǎn)優(yōu)勢(shì)

  • Proxy 是對(duì)整個(gè)對(duì)象的代理,而 Object.defineProperty 只能代理某個(gè)屬性
  • 對(duì)象上新增屬性,Proxy 可以監(jiān)聽到,Object.defineProperty 不能
  • 數(shù)組新增修改,Proxy 可以監(jiān)聽到,Object.defineProperty 不能
  • 若對(duì)象內(nèi)部屬性要全部遞歸代理,Proxy 可以只在調(diào)用的時(shí)候遞歸,而 Object.definePropery 需要一次完成所有遞歸,性能比 Proxy 差

使用也很簡單,Proxy本質(zhì)是構(gòu)造函數(shù),通過new即可產(chǎn)生對(duì)象,它接收兩個(gè)參數(shù):

  • target表示的就是要攔截(代理)的目標(biāo)對(duì)象
  • handler是用來定制攔截行為(13種

例如響應(yīng)式reactive的基本實(shí)現(xiàn):

function reactive(obj) {
    return new Proxy(obj, {
        get(target, key) {
            // 可以做依賴收集
            track(target, key)
            return target[key]
        },
        set(target, key, val) {
            target[key] = val
            // 觸發(fā)依賴
            trigger(target, key)
        }
    })
}

瀏覽器支持情況


6.jpg

7. Promise.any快速獲取一組Promise實(shí)例中第一個(gè)fulfilledpromise

Promise.any 接收一組Promise實(shí)例作為參數(shù)

  • 只要其中的一個(gè) promise 成功,就返回那個(gè)已經(jīng)成功的 promise
  • 如果這組可迭代對(duì)象中,沒有一個(gè) promise 成功,就返回一個(gè)失敗的 promiseAggregateError 類型的實(shí)例

寫法推薦

try {
  const first = await Promise.any(promises);
  // Any of the promises was fulfilled.
} catch (error) {
  // All of the promises were rejected.
}

或者

Promise.any(promises).then(
  (first) => {
    // Any of the promises was fulfilled.
  },
  (error) => {
    // All of the promises were rejected.
  }
);

瀏覽器支持情況

7.jpg

8. 使用BigInt支持大整數(shù)計(jì)算問題

ES2020[1] 引入了一種新的數(shù)據(jù)類型 BigInt,用來表示任意位數(shù)的整數(shù)

例如

// 超過 53 個(gè)二進(jìn)制位的數(shù)值(相當(dāng)于 16 個(gè)十進(jìn)制位),無法保持精度
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true

// BigInt
BigInt(Math.pow(2, 53)) === BigInt(Math.pow(2, 53)) + BigInt(1) // false

除了使用BigInt來聲明一個(gè)大整數(shù),還可以使用數(shù)字后面加n的形式,如

1234 // 普通整數(shù)1234n // BigInt復(fù)制代碼

需要了解BigInt數(shù)字操作時(shí)的支持情況,以免踩坑

操作 是否支持
單目 (+) 運(yùn)算符 N
+、*、-、**、% 運(yùn)算符 Y
\ 除法運(yùn)算符 帶小數(shù)的運(yùn)算會(huì)被取整
>>> 無符號(hào)右移位操作符 N
其他位移操作符 Y
與 Number 混合運(yùn)算 N(必須轉(zhuǎn)換為同類型)
Math 對(duì)象方法 N
Number 與 BigInt 比較(排序) Y(寬松相等 ==)
Boolean 表現(xiàn) 類型 Number 對(duì)象
JSON 中使用 N

瀏覽器支持情況

8.jpg

9. 使用Array.prototype.at()簡化arr.length

Array.prototype.at()接收一個(gè)正整數(shù)或者負(fù)整數(shù)作為參數(shù),表示獲取指定位置的成員

參數(shù)正數(shù)就表示順數(shù)第幾個(gè),負(fù)數(shù)表示倒數(shù)第幾個(gè),這可以很方便的某個(gè)數(shù)組末尾的元素

例如

var arr = [1, 2, 3, 4, 5]
// 以前獲取最后一位
console.log(arr[arr.length-1]) //5
// 簡化后
console.log(arr.at(-1)) // 5

10. 使用哈希前綴#將類字段設(shè)為私有

在類中通過哈希前綴#標(biāo)記的字段都將被私有,子類實(shí)例將無法繼承

例如

class ClassWithPrivateField {
    #privateField;
    #privateMethod() {
        return 'hello world';
    }
    constructor() {
        this.#privateField = 42;
    }
}

const instance = new ClassWithPrivateField()
console.log(instance.privateField); //undefined
console.log(instance.privateMethod); //undefined

可以看到,屬性privateField和方法privateMethod都被私有化了,在實(shí)例中無法獲取到

最后

很多新特性都有很多人在用了,特別是???.以及動(dòng)態(tài)引入import(),不知道你都用過哪些?

好了,以上就是本文的相關(guān)內(nèi)容,如有問題歡迎指出~??

作者:LBJ

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容