一起來看看 Node.js v14.x LTS 中的這些新功能

2021-02-20 Nodejs技術棧

作者簡介:五月君,Software Designer,公眾號「Nodejs技術棧」作者。

Node.js 是一個基於 Chrome V8 引擎的 JavaScript 運行時。在 2020 年 10 月 27 日 Node.js v14.15.0 LTS 版已發布,即長期支持版本,其中包含了很多很棒的新功能,以下內容也是基於筆者在日常 Node.js 工作和學習中所總結的,可能不全,同時也歡迎補充,有些功能之前也曾單獨寫過文章來介紹,接下讓我們一起看看都有哪些新的變化?

目錄Intl.DisplayNames(國際化顯示名稱)Intl.DateTimeFormat(國際化處理日期時間格式)String.prototype.matchAll (throws on non-global regex)Async Local Storage(異步本地存儲)Top-Level Await(頂級 await 支持)Optional Chaining(可選鏈)

如果我們使用 JavaScript 不管是用在前端或者 Node.js 服務端都會出現如下情況,因為我們有時是不確定 user 對象是否存在,又或者 user 對象裡面的 address 是否存在,如果不這樣判斷, 可能會得到類似於 Cannot read property 'xxx' of undefined 這樣的類似錯誤

const user = {
  name: 'Tom',
  address: {
    city: 'ZhengZhou'
  }
}
if (user && user.address) {
  console.log(user.address.city)
}

現在我們有一種優雅的寫法 "可選鏈操作符",不必明確的驗證鏈中的每個引用是否有效,以符號 "?." 表示,在引用為 null 或 undefined 時不會報錯,會發生短路返回 undefined。

user.address?.city
user.address?.city?.length

// 結合 ?.[] 的方式訪問相當於 user.address['city']
user.address?.['city']

// 結合 delete 語句使用,僅在 user.address.city 存在才刪除
delete user.address?.city

參考 v8.dev/features/optional-chaining[1]

Nullish Coalescing(空值合併)

邏輯或操作符(||)會在左側為假值時返回右側的操作符,例如我們傳入一個屬性為 enabled:0 我們期望輸出左側的值,則是不行的。

function Component(props) {
  const enable = props.enabled || true; // true
}
Component({ enabled: 0 })

現在我們可以使用 **空值合併操作符(??)**來實現,僅當左側為 undefined 或 null 時才返回右側的值。

function Component(props) {
  const enable = props.enabled ?? true; // 0
}

Component({ enabled: 0 })

參考:v8.dev/features/nullish-coalescing[2]

Intl.DisplayNames

對於國際化應用需要用到的語言、區域、貨幣、腳本的名稱,現在 JavaScript 開發者可以使用 Intl.DisplayNames API 直接訪問這些翻譯,使應用程式更輕鬆的顯示本地化名稱。

Language(語言)
let longLanguageNames = new Intl.DisplayNames(['zh-CN'], { type: 'language' });
longLanguageNames.of('en-US'); // 美國英語
longLanguageNames.of('zh-CN'); // 中文(中國)

longLanguageNames = new Intl.DisplayNames(['en'], { type: 'language' });
longLanguageNames.of('en-US'); // American English
longLanguageNames.of('zh-CN'); // Chinese (China)

Region(區域)
let regionNames = new Intl.DisplayNames(['zh-CN'], {type: 'region'});
regionNames.of('US'); // 美國
regionNames.of('419'); // 拉丁美洲

regionNames = new Intl.DisplayNames(['en'], {type: 'region'});
regionNames.of('US'); // United States
regionNames.of('419'); // Latin America

Currency(貨幣)
let currencyNames = new Intl.DisplayNames(['zh-CN'], {type: 'currency'});
currencyNames.of('CNY'); // 人民幣
currencyNames.of('USD'); // 美元

currencyNames = new Intl.DisplayNames(['en'], {type: 'currency'});
currencyNames.of('CNY'); // Chinese Yuan
currencyNames.of('USD'); // US Dollar

Script(腳本)
let scriptNames = new Intl.DisplayNames(['zh-CN'], {type: 'script'});
scriptNames.of('Hans'); // 簡體
scriptNames.of('Latn'); // 拉丁文

scriptNames = new Intl.DisplayNames(['en'], {type: 'script'});
scriptNames.of('Hans'); // Simplified
scriptNames.of('Latn'); // Latin

參考:v8.dev/features/intl-displaynames[3] 上述實例用到的國家代號和 code 都可從參考地址獲取。

Intl.DateTimeFormat

Intl.DateTimeFormat API 用來處理特定語言環境的日期格式。

const date = new Date();

// Sunday, January 10, 2021 at 9:02:29 PM GMT+8
new Intl.DateTimeFormat('en-US', { dateStyle: 'full', timeStyle: 'long'}).format(date)

// 21/1/10 中國標準時間 下午9:02:29.315
new Intl.DateTimeFormat('zh-CN', {
  year: '2-digit',
  month: 'numeric',
  day: 'numeric',
  hour: 'numeric',
  minute: 'numeric',
  second: 'numeric',
  fractionalSecondDigits: 3,
  timeZoneName: 'long'
}).format(date)

參考: Intl/DateTimeFormat[4]

String.prototype.matchAll

matchAll() 返回一個包含所有匹配正則表達式的結果,返回值為一個不可重用(不可重用意思為讀取完之後需要再次獲取)的迭代器。

matchAll() 方法在 Node.js v12.4.0 以上版本已支持,該方法有個限制,如果設置的正則表達式沒有包含全局模式 g ,在 Node.js v14.5.0 之後的版本如果沒有提供會拋出一個 TypeError 異常。

// const regexp = RegExp('foo[a-z]*','g'); // 正確
const regexp = RegExp('foo[a-z]*'); // 錯誤,沒有加全局模式
const str = 'table football, foosball, fo';
const matches = str.matchAll(regexp); // TypeError: String.prototype.matchAll called with a non-global RegExp argument

for (const item of matches) {
  console.log(item);
}

參考:ES2020-features-String-prototype-matchAll-throws-on-non-global-regex[5]

AsyncLocalStorage

Node.js Async Hooks 模塊提供了 API 用來追蹤 Node.js 程序中異步資源的聲明周期,在最新的 v14.x LTS 版本中新增加了一個 AsyncLocalStorage 類可以方便實現上下文本地存儲,在異步調用之間共享數據,對於實現日誌鏈路追蹤場景很有用。

下面是一個 HTTP 請求的簡單示例,模擬了異步處理,並且在日誌輸出時去追蹤存儲的 id。

const http = require('http');
const { AsyncLocalStorage } = require('async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();
function logWithId(msg) {
  const id = asyncLocalStorage.getStore();
  console.log(`${id !== undefined ? id : '-'}:`, msg);
}
let idSeq = 0;
http.createServer((req, res) => {
  asyncLocalStorage.run(idSeq++, () => {
    logWithId('start');
    setImmediate(() => {
      logWithId('processing...');
      setTimeout(() => {
        logWithId('finish');
        res.end();
      }, 2000)
    });
  });
}).listen(8080);

下面是運行結果,我在第一次調用之後直接調用了第二次,可以看到我們存儲的 id 信息與我們的日誌一起成功的列印了出來。

image.png

便利性的同時也會犧牲一些性能上的代價,關於 AsyncLocalStorage 的詳細使用介紹參見筆者的另一篇文章 「在 Node.js 中使用 async hooks 模塊的 AsyncLocalStorage 類處理請求上下文」 中的介紹。

ES Modules 支持

ES Modules 的支持總體上來說是個好事,進一步的規範了 Node.js 與瀏覽器的模塊生態,使之進一步趨同,同時避免了進一步的分裂。

在當前 Node.js v14.x LTS 版本中已移除試驗性支持,現在使用無需使用標誌了,它使用 import、export 關鍵字,兩種使用方式:

使用 .mjs 擴展名
// caculator.mjs
export function add (a, b) {
  return a + b;
};

// index.mjs
import { add } from './caculator.js';

console.log(add(4, 2)); // 6

告訴 Node.js 將 JavaScript 代碼視為 ES Modules

默認情況下 Node.js 將 JavaScript 代碼視為 CommonJS 規範,所以我們要在上面使用擴展名為 .mjs 的方式來聲明,除此之外我們還可以在 package.json 文件中 設置 type 欄位為 module 或在運行 node 時加上標誌 --input-type=module 告訴 Node.js 將 JavaScript 代碼視為 ES Modules。

// package.json
{
  "name": "esm-project",
  "type": "module",
  ...
}

前端的同學可能會對以上使用 ES Modules 的方式很熟悉。

詳細使用參見筆者在文章 「在 Nodejs 中 ES Modules 使用入門講解」 中的介紹。

Top-Level Await

頂級 await 支持在異步函數之外使用 await 關鍵字,在 Node.js v14.x LTS 版本中已去掉試驗性支持,現在使用也不再需要設置標誌。

import fetch from 'node-fetch';
const res = await fetch(url)

也可以像調用函數一樣動態的導入模塊。

const myModule = await import('./my-module.js');

對於異步資源,之前我們必須在 async 函數內才可使用 await,這對一些在文件頂部需要實例化的資源可能會不好操作,現在有了頂級 await 我們可以方便的在文件頂部對這些異步資源做一些初始化操作。

詳細使用參見筆者在文章 「Nodejs v14.3.0 發布支持頂級 Await 和 REPL 增強功能」 中的介紹。

Diagnostic report(診斷報告)

Diagnostic report 是 Node.js v14.x LTS 提供的一個穩定功能,在某些情況下會生成一個 JSON 格式的診斷報告,可用於開發、測試、生產環境。報告會提供有價值的信息,包括:JavaScript 和本機堆棧信息、堆統計信息、平臺信息、資源使用情況等,幫助用戶快速追蹤問題。

https://github.com/IBM/report-toolkit[6] 是 IBM 開發的一個款工具,用於簡化報告工具的使用,如下是一個簡單 Demo 它會造成服務的內存洩漏。

const total = [];
setInterval(function() {
  total.push(new Array(20 * 1024 * 1024)); // 大內存佔用,不會被釋放
}, 1000)

最終生成的 JSON 報告被 report-toolkit 工具診斷的結果可能是下面這樣的。

image.png

詳細使用參見筆者在文章 「在 Node.js 中使用診斷報告快速追蹤問題」 中的介紹。

Stream

新版本中包含了對 Stream 的一些更改,旨在提高 Stream API 的一致性,以消除歧義並簡化 Node.js 核心各個部分的行為,例如:

http.OutgoingMessage 與 stream.Writable 類似net.Socket 的行為與 stream.Duplex 完全相同一個顯著的變化 autoDestroy 的默認值為 true,使流在結束之後始終調用 _destroy

參考:Node.js version 14 available now#Stream [7]

使用異步迭代器

使用異步迭代器我們可以對 Node.js 中的事件、Stream 亦或者 MongoDB 返回數據遍歷,這是一件很有意思的事情,儘管它不是 Node.js v14.x 中新提出的功能,例如 event.on 是在 Node.js v12.16.0 才支持的,這些目前看到的介紹還不太多,因此我想在這裡做下簡單介紹。

在 Events 中使用

Node.js v12.16.0 中新增了 events.on(emitter, eventName) 方法,返回一個迭代 eventName 事件的異步迭代器,例如啟動一個 Node.js 服務可以如下這樣寫,想知道它的原理的可以看筆者下面提到的相關文章介紹。

import { createServer as server } from 'http';
import { on } from 'events';
const ee = on(server().listen(3000), 'request');
for await (const [{ url }, res] of ee)
  if (url === '/hello')
    res.end('Hello Node.js!');
  else
    res.end('OK!');

在 Stream 中使用

以往我們可以通過 on('data') 以事件監聽的方式讀取數據,通過異步迭代器可以一種更簡單的方式實現。

async function readText(readable) {
  let data = '';
  for await (const chunk of readable) {
    data += chunk;
  }
  return data;
}

目前在 JavaScript 中還沒有被默認設定 [Symbol.asyncIterator] 屬性的內建對象,在 Node.js 的一些模塊 Events、Stream 中是可使用的,另外你還可以用它來遍歷 MongoDB 的返回結果。

關於異步迭代器詳細使用參見筆者在文章 「探索異步迭代器在 Node.js 中的使用」 中的介紹。

參考資料[1]

v8.dev/features/optional-chaining: https://v8.dev/features/optional-chaining

[2]

v8.dev/features/nullish-coalescing: https://v8.dev/features/nullish-coalescing

[3]

v8.dev/features/intl-displaynames: https://v8.dev/features/intl-displaynames

[4]

Intl/DateTimeFormat: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat

[5]

ES2020-features-String-prototype-matchAll-throws-on-non-global-regex: https://node.green/#ES2020-features-String-prototype-matchAll-throws-on-non-global-regex

[6]

https://github.com/IBM/report-toolkit: https://github.com/IBM/report-toolkit

[7]

Node.js version 14 available now#Stream : https://nodejs.medium.com/node-js-version-14-available-now-8170d384567e

相關焦點

  • Node.js v14 官方發布說明來了
    在這裡了解如何下載最新版本:https://nodejs.org/en/download/current/在深入了解這一版本的新功能之前,請務必注意,添加到主版中的新功能會迅速流入當前版本。這意味著重要功能可以在次要版本中使用。我們想藉此機會重點介紹 Node.js 14 版本中的某些功能,即使它們可能已經被移植到較早的版本中了。
  • dotNet 5 中執行 Node.js
    環境dotNET 5 中的代碼實現1、在 VS 中創建 WebAPI 示例項目 nodejs-demo 項目,目標框架選擇 .NET 5.0。1、在 Node.js 的中文官網 (http://nodejs.cn/download/current/) 下載 Linux 二進位文件 (x64) 的安裝包;2、將下載的安裝包 node-v14.17.4-linux-x64.tar.xz 複製到 CentOS 系統的 root 目錄中;
  • 在 Nodejs 中 ES Modules 使用入門講解
    /caculator.js';console.log(add(4, 2)); // 6運行與當前的 v14.3.0 不同的是在 v12.17.0 中使用 ESM 運行時仍然會觸發一個 experimental 警告信息。
  • Centos7下使用 nvm 安裝 nodejs 環境
    "$NVM_DIR/bash_completion" # This loads nvm bash_completion[root@dev ~]#根據提示可以看到腳本已經將相關環境變量的內容加入到了/root/.bashrc文件中,下面來看看,如下:image-20200811152141839設置一下環境變量生效
  • Nodejs 14 大版本中新增特性總結
    /#ES2020-features-String-prototype-matchAll-throws-on-non-global-regexAsync Local Storage(異步本地存儲)Node.js Async Hooks 模塊提供了 API 用來追蹤 Node.js 程序中異步資源的聲明周期,在最新的 v14.x LTS 版本中新增加了一個
  • Nodejs v14.3.0 發布支持頂級 Await 和 REPL 增強功能
    本周,Nodejs v14.3.0 發布。這個版本包括添加頂級 Await、REPL 增強等功能。REPL 增強通過自動補全改進對 REPL 的預覽支持,例如,下圖中當輸入 process.ver 之後,不需要輸入剩下的實際內容,它幫我們生成了自動補全的輸入預覽。
  • linux環境下安裝nodejs
    Node.js發布於2009年5月,由Ryan Dahl開發,是一個基於Chrome V8引擎的JavaScript運行環境,使用了一個事件驅動
  • 在 Node.js 中使用診斷報告快速追蹤問題
    Diagnostic report 是 Node.js v14.x 提供的一個穩定功能,在某些情況下會生成一個 JSON 格式的診斷報告,可用於開發、測試、生產環境。報告會提供有價值的信息,包括:JavaScript 和本機堆棧信息、堆統計信息、平臺信息、資源使用情況等,幫助用戶快速追蹤問題。
  • Node.js系列一 - JavaScript運行原理
    Atwood定律我們先看一下官方對Node.js的定義:Node.js是一個基於V8 JavaScript引擎的JavaScript運行時環境。但是這句話對於很多同學來說,非常籠統:我們先來把這些概念搞清楚,再去看Node到底是什麼?
  • Node.js的下載和安裝教程
    1,Node.js安裝包的下載登錄Node.js中文官網,https://nodejs.org/zh-cn/,點擊下載14.15.4長期支持版。還可以根據電腦系統選擇合適的Node.js版本進行下載,如Win10 64位系統,選擇Windows安裝包(.msi) 64-bit進行下載。
  • centos7.6部署安裝Wikijs
    寫在前面環境wiki安裝路徑:/var/wikinode安裝路徑:/usr/local/node安裝軟體前,先看看官網的說明文檔
  • 運維的報表之路,用 node.js 輕鬆發送 grafana 報表
    我們通過 nodejs 的模塊 puppeteer + nodemailer 實現了grafana pannel 圖表的郵件報表功能,基本滿足了我們在自動化報表這塊的需求。/dist/node/v14.3.0/node-v14.3.0-linux-x64.tar.xztar -xf node-v14.3.0-linux-x64.tar.xzvi /etc/profile#set for nodejsexport NODE_HOME=/opt/node-v14.3.0-linux-x64export PATH=$NODE_HOME/bin:$PATHsource
  • Node.js TLSWrap 實現中的釋放後使用漏洞分析
    Node v14.11.0 版本的 TLS 實現中存在一個釋放後使用漏洞。當寫入啟用 TLS 的套接字時,node::StreamBase::Write 調用 node::TLSWrap:DoWrite,其第一個參數為新分配的 WriteWrap 對象。
  • Node.js:10個最有用和有趣的新功能
    儘管Node.js debugger 非常的實用, 這些年它也沒有被很好的關注,現代瀏覽器中的 Javascript debuggers 已經比 Node 自己能夠提供的先進很多了。在 Node.js 6.3.0 中,Google 的 v8_inspector 協議來自 Blink,並隨 Node 提供。
  • NVM安裝Node.js
    過後突然想起之前學的前端視頻,有提到過要對開發前端項目時,要對node.js的版本進行管理。果然他是下了一個最新node.js版本進行編譯運行,降低了版本後,項目就正常編譯運行了。前端最近幾年發展尤為迅速,版本迭代也很多,很多以前的項目是依賴於不同版本的node.js,於是為了方便處理各版本的node.js切換,就不能直接安裝node.js,需要通過NVM來進行安裝。
  • 極簡 Node.js入門 教程-模塊
    (不包含環境變量)參數0: /Users/a/.nvm/versions/node/v6.1.0/bin/node參數1: /Users/a/Documents/git-code/nodejs-learning-guide/examples/2016.11.22-node-process/argv.js參數2: --env參數3: production
  • Node.js的安裝和環境配置(2020)
    是否安裝成功(1)打開cmd命令符,直接輸入node -v   出現版本號即可 我的版本號 v14.13.1如果不配置的話,默認【npm install 模塊名 -g】是安裝到【C:\Users\用戶名\AppData\Roaming\npm】路徑中,而此處希望將全局模塊和緩存路徑放到Node.js的安裝目錄下【D:\Soft\NodeJs】。
  • 版本發布:v14.18.0
    2021.09.28,Node.js 發布了 v14.18.0 版本,更新如下:(建議橫屏閱讀,體驗更佳)「命令行選項
  • OrangePi Zero2測評|為家居智能化構建環境(MQTT Broker和Node-Red部署)
    可以看到,成功的訂閱了消息,成功的發布一條消息,同時也收到了訂閱的消息內容。移動到/opt目錄下 mv node-v14.15.4-linux-arm64 /opt部署執行如下命令創建軟連接        ln –s /opt/ node-v14.15.4-linux-arm64/bin/node  /usr/