《What’s new in Javascript》簡(jiǎn)報(bào)

這周看了google IO上的一個(gè)視頻,講JS的新feature。還是挺有意思的,所以這次就做個(gè)“簡(jiǎn)報(bào)”,列一下值得一看的新feature。

Private Field

自從JS引進(jìn)class語(yǔ)法糖以來(lái),私有域的實(shí)現(xiàn)一直是個(gè)有爭(zhēng)議的話題。我記得以前的面試題還喜歡考《私有域有幾種實(shí)現(xiàn)方式?》——Symbol、WeakMap、Proxy等幾種“奇技淫巧”。今年TC39正式將#作為標(biāo)志符更新到Stage 3,雖然沒(méi)有官宣,但是V8已經(jīng)開始實(shí)驗(yàn)性地支持這個(gè)新特性了。

class Counter {
    #counter = 0;
    get value() {
        return #counter;
    }
}

const c = new Counter();

c.value // 0
c.#counter; // Syntax Error

如上,#counter就是Counter類的私有域,外部調(diào)用時(shí)會(huì)報(bào)出語(yǔ)法錯(cuò)誤。用慣了typescript的private,我看到#時(shí)還是覺(jué)得怪怪的。想嘗鮮的朋友,可以安裝最新版的Chrome或Node試驗(yàn)。相傳私有方法,私有g(shù)et和set很快也會(huì)被加入進(jìn)來(lái)。

Big Int

Big Int顧名思義大整數(shù),我們來(lái)做個(gè)計(jì)算題1234567890123456789 * 123,看看js的計(jì)算結(jié)果:151851850485185200000。有點(diǎn)長(zhǎng),不看別的就看末尾數(shù)0,顯然計(jì)算結(jié)果是錯(cuò)的。原因還是在JS數(shù)字精度丟失這一經(jīng)典問(wèn)題上,JS的安全精度在(-2^53, 2^53),超出了安全范圍就不準(zhǔn)了。

console.log(1234567890123456789 * 123) // 151851850485185200000

再看看新語(yǔ)法,末尾加個(gè)n,答案是151851850485185185047n,Bingo。

console.log(1234567890123456789n * 123n) // 151851850485185185047n

Array Flat & FlatMap

我個(gè)人是從java8才開始知道有flat的概念(嗯,就是這么后知);這個(gè)概念來(lái)自函數(shù)式編程,如果要講歷史典故得追溯到上個(gè)世紀(jì)中葉了。

  • 新版JS中,flat()被設(shè)計(jì)為高維數(shù)組一維化(降維打擊??)。

    var newArray = arr.flat(depth)
    
    • 參數(shù)depth: 指定要提取嵌套數(shù)組的結(jié)構(gòu)深度,默認(rèn)值為 1。
    • 返回值: 一個(gè)包含將數(shù)組與子數(shù)組中所有元素的新數(shù)組。
    const higherDimensionalArray = [ "a", ["b", "c"], ["d", ["e", "f"]]];
    higherDimensionalArray.flat( 2 );  // [ "a", "b", "c", "d", "e", "f" ]
    
  • flatMapmap很像,不同之處是flatMap會(huì)對(duì)回調(diào)結(jié)果一維化:

    
    const scattered =  [ "my favorite", "fruit is", "red bayberry" ];
    
    scattered.map( chunk => chunk.split(" ") ); // [["my", "favorite"], ["fruit", "is"], ["red", "bayberry"]]
    
    scattered.flatMap( chunk => chunk.split(" ") ); // ["my", "favorite", "fruit", "is", "red", "bayberry"]
    

Object.fromEntries

Object.fromEntriesObject.entries反向函數(shù):


let obj = {k1: 'v1', k2: 'v2', k3: 'v3'};

let entries = Object.entries(obj); // [['k1','v1'],['k2','v2'],['k3','v3']]

Object.fromEntries(entries); // {k1: 'v1', k2: 'v2', k3: 'v3'}

雖然互為反向,但是fromEntriesentries晚出了三四年,也挺奇怪的一件事。

Optional Catch Binding

允許開發(fā)者省略catch里的參數(shù)e,但好像也沒(méi)什么重大意義(汗)

try {
    throw new Error('some error');
} catch {
    console.log('no params for catch');
}

早期版本:

try {
    throw new Error('some error');
} catch(e) {
    console.log(e); //  Error: "some error"
}

WeakRef

在WeakRef前,ES6中就有兩個(gè)Weak類了——WeakMap和WeakSet。我曾經(jīng)寫過(guò)一篇《ES6之WeakMap》,有興趣的朋友可以看一下。我們回顧一下JS垃圾回收機(jī)制,它主要通過(guò)“引用標(biāo)記”和“引用清除”兩個(gè)方法實(shí)現(xiàn)內(nèi)存回收。每當(dāng)對(duì)象多一次引用則“引用數(shù)”加1,少一次則減一;當(dāng)“引用數(shù)”為0時(shí),啟動(dòng)垃圾回收。以WeakMap為例,它存儲(chǔ)的對(duì)象都是弱引用,不會(huì)增加“引用數(shù)”,因此不會(huì)導(dǎo)致內(nèi)存溢出。看兩個(gè)例子:

//map.js
function memoryUsage() {
    const used = process.memoryUsage().heapUsed;
    console.log( Math.round(used / 1024 / 1024) + 'M' );
}

memoryUsage(); // ≈ 4M

let arr = new Array(1024 * 1024);
const map = new Map();

map.set(arr, 1);
global.gc();
memoryUsage(); // ≈ 12M

arr = null;
global.gc();
memoryUsage(); // ≈ 12M
//weakmap.js
memoryUsage(); // ≈ 4M

let arr = new Array(1024 * 1024);
const map = new WeakMap();

map.set(arr, 1);
global.gc();
memoryUsage(); // ≈ 12M

arr = null;
global.gc();
memoryUsage(); // ≈ 4M

分別執(zhí)行node --expose-gc map.jsnode --expose-gc weakmap.js??梢院苊黠@地看到區(qū)別:在arr被置為null后,Map并沒(méi)有釋放Array,而WeakMap釋放了。原因正如上文所示:Map是強(qiáng)引用,arr清除后依舊保留了對(duì)new Array(1024 * 1024)的引用指向,而WeakMap并沒(méi)有保留,因此垃圾回收機(jī)制可以照常執(zhí)行。

再回到WeakRef。WeakRef也是不增加“引用數(shù)”的。我們來(lái)看看tc93上的介紹:

  • WeakRef通過(guò)傳入Object直接構(gòu)造new WeakRef({})
  • 它有一個(gè)唯一的方法deref返回構(gòu)造時(shí)傳入的對(duì)象;若對(duì)象已被回收,則返回undefined

舉個(gè)簡(jiǎn)單的例子,假如我們想對(duì)一個(gè)圖片(一般來(lái)說(shuō)是ArrayBuffer)做緩存,你很可能希望通過(guò)文件名去讀取該對(duì)象。直接使用Map很可能導(dǎo)致內(nèi)存溢出,但是WeakMap也不合適——它的key只能是object。在這個(gè)場(chǎng)景里WeakRef是很好的折衷手段,只需要“虛化”map的value值:我們既不需要在內(nèi)存中強(qiáng)引用巨大的ArrayBuffer,也可以使用string作為鍵值;當(dāng)ArrayBuffer被垃圾回收后,Map里只有一個(gè)很小的空WeakRef指向。如下:我們將getImage中獲得的ArrayBuffer虛引用后存入cache;正常情況下可以快速獲得image引用,當(dāng)外部作用域清除image的ArrayBuffer后,cache中就只能獲取一個(gè)undefined的WeakRef了,我們不用過(guò)多擔(dān)心內(nèi)存泄漏了。

function makeWeakCached(f) {
  const cache = new Map();
  return (key) => {
    const ref = cache.get(key);
    if (ref) {
      const cached = ref.deref();
      if (cached !== undefined) return cached;
    }

    const fresh = f(key);
    cache.set(key, new WeakRef(fresh));
    return fresh;
  };
}

var getImageCached = makeWeakCached(getImage);

Promise

現(xiàn)代開發(fā)中,早已大量使用async/await語(yǔ)法糖了,很多新人可能并不是很了解Promise了。我們這種ES5過(guò)來(lái)的人,對(duì)Promise還是挺有印象的(被polyfill惡心過(guò)有數(shù)了)。尤其是Promise.all()Promise.race()這種方法我現(xiàn)在還是經(jīng)常使用的。這次又新增了Promise.allSettled()Promsie.any()。這兩個(gè)方法我很早就在《You don't know JS》里看到過(guò),概念可能已經(jīng)源遠(yuǎn)流長(zhǎng)了。具體功能如可以從函數(shù)名猜出大概:allSettled與all相對(duì),表明全部Promise執(zhí)行完后再返回,不似all只要有一個(gè)錯(cuò)誤直接reject;any和race相對(duì),表明只要有一個(gè)Promise fulfilled就返回then,只有全部reject才拋異常。

其他

再快速過(guò)一下其他幾個(gè)小更新

  • Array.Sort

    以前V8實(shí)現(xiàn)數(shù)組排序是:10個(gè)元素以上是穩(wěn)定排序,10以下是不穩(wěn)定排序,現(xiàn)在改成全是穩(wěn)定排序了

  • String.trimStart() & String.trimEnd()

    顧名思義,豐富了一下trim()函數(shù)的使用場(chǎng)景

  • Function.toString()

    Function也有toString方法了,可以打印函數(shù)源碼,不受高編影響

  • Symbol.description

    竟然沒(méi)發(fā)現(xiàn)以前是無(wú)法打印Symbol value值的(汗)

    var s = Symbol('Onion')
    console.log(s.description) // Onion
    
  • Numeric Seperator

    數(shù)字分隔符支持,挺常見的需求

    let a = 1_000_000
    let b = 1_019.42
    

其實(shí)我最想看到的是Optional Chaining,可惜還在Stage 1。

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

var oc_streat = user?.address?.stree;

小結(jié)

最后再推一波《What’s New in JavaScript》油管視頻。它還提到了其他很多有趣的新特性,有興趣的朋友點(diǎn)進(jìn)去看一下,英語(yǔ)也沒(méi)啥難度。

金三銀四過(guò)后,就是老同事們各奔東西了;當(dāng)年一起寫JS大前端的同僚,今天只剩我一人了。我倒不需要像管理層那樣精算人力,只是回想起自己這幾年工作經(jīng)歷——滄海桑田。眼看他起朱樓,眼看他宴賓客,眼看他。。。不可妄議了。人來(lái)人往,世間無(wú)不散之宴席,祝大家一切安好。

相關(guān)博客

最后編輯于
?著作權(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),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 概要 64學(xué)時(shí) 3.5學(xué)分 章節(jié)安排 電子商務(wù)網(wǎng)站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,880評(píng)論 0 3
  • JavaScript語(yǔ)言精粹 前言 約定:=> 表示參考相關(guān)文章或書籍; JS是JavaScript的縮寫。 本書...
    微笑的AK47閱讀 663評(píng)論 0 3
  • # 數(shù)組部分 # 1.## array_chunk($arr, $size [, $preserve_key = ...
    clothTiger閱讀 1,326評(píng)論 0 1
  • JS基礎(chǔ) 頁(yè)面由三部分組成:html:超文本標(biāo)記語(yǔ)言,負(fù)責(zé)頁(yè)面結(jié)構(gòu)css:層疊樣式表,負(fù)責(zé)頁(yè)面樣式j(luò)s:輕量級(jí)的腳...
    小賢筆記閱讀 679評(píng)論 0 5
  • 臺(tái)風(fēng)又入侵 以千只馬匹狂奔之聲 以海卷波濤抵天之態(tài) 屋子也全力與臺(tái)風(fēng)對(duì)抗 樹間的吊床卻弱不經(jīng)風(fēng) 大地上的一切 全在...
    喜樂(lè)心記閱讀 171評(píng)論 0 0

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