让我们来看看Node.jsv14.xLTS中的这些新特性Node.js是一个基于ChromeV8引擎的JavaScript运行时。2020年10月27日,Node.jsv14.15.0LTS版本发布,这是长期支持版本,包含许多很棒的新特性。以下内容也是基于笔者日常的Node.js工作和学习。总结,可能不完整,欢迎补充。之前已经单独写了一些功能。让我们来看看新的变化。CatalogOptionalChainingNullishCoalescingNullishCoalescingIntl.DisplayNames(国际化显示名称)Intl.DateTimeFormat(国际化日期时间格式)String.prototype.matchAll(throwsonnon-globalregex)AsyncLocalStorage(异步本地存储)ESModulessupportTop-LevelAwait(顶层等待支持)Diagnosticreport(诊断报告)Stream使用异步迭代器OptionalChaining(可选链)如果我们使用JavaScript,无论是在前端还是在Node.js服务器上使用,都会出现以下情况,因为有时候我们不确定用户对象是否存在,或者用户对象中的地址是否存在,如果不这样判断,可能会出现类似Cannotreadproperty'xxx'ofundefined的错误。constuser={name:'Tom',address:{city:'ZhengZhou'}}if(user&&user.address){console.log(user.address.city)}现在我们有了一种优雅的写法“可选链运算符”,不需要显式验证链中的每个引用是否有效,用符号“?.”表示,当引用为null或undefined时不会报错,短路会返回undefined发生。user.address?.cityuser.address?.city?.length//与?.[]结合访问相当于user.address['city']user.address?.['city']//结合使用withdeletestatement,deleteuser.address?.cityonlyifuser.address.cityexists返回假值时,返回右边的运算符。例如,如果我们传入一个属性为enabled:0并且我们期望输出左边的值,它就不起作用。functionComponent(props){constenable=props.enabled||true;//true}Component({enabled:0})现在我们可以使用**空值合并运算符(??)**来实现,只要左边的右侧的值仅在未定义或null时返回。functionComponent(props){constenable=props.enabled??true;//0}Component({enabled:0})参考:v8.dev/features/nullish-coalescing[2]国际化应用语言需要Intl.DisplayNames,地区、货币和脚本名称,JavaScript开发人员现在可以使用Intl.DisplayNamesAPI直接访问这些翻译,使应用程序更容易显示本地化名称。Language(语言)letlongLanguageNames=newIntl.DisplayNames(['zh-CN'],{type:'language'});longLanguageNames.of('en-US');//美国英语longLanguageNames.of('zh-CN'');//中文(中国)longLanguageNames=newIntl.DisplayNames(['en'],{type:'language'});longLanguageNames.of('en-US');//AmericanEnglishlongLanguageNames.of('zh-CN');//Chinese(China)Region(地区)letregionNames=newIntl.DisplayNames(['zh-CN'],{type:'region'});regionNames.of('US');//美国regionNames.of('419');//拉丁美国regionNames=newIntl.DisplayNames(['en'],{type:'region'});regionNames.of('US');//UnitedStatesregionNames.of('419');//LatinAmericaCurrency(货币)letcurrencyNames=newIntl.DisplayNames(['zh-CN'],{type:'currency'});currencyNames.of('CNY');??//人民币currencyNames.of('USD');//美元currencyNames=newIntl.DisplayNames(['en'],{type:'currency'});currencyNames.of('CNY');??//ChineseYuancurrencyNames.of('USD');//USDollarScript(脚本)letscriptNames=newIntl.DisplayNames(['zh-CN'],{type:'script'});scriptNames.of('Hans');//简体scriptNames.of('Latn');//LatinscriptNames=newIntl.DisplayNames(['en'],{type:'script'});scriptNames.of('Hans');//SimplifiedscriptNames.of('Latn');//拉丁文参考:v8.dev/features/intl-displaynames[3]上面示例中使用的国家代码和代码可以从参考地址获取Intl.DateTimeFormatIntl??.DateTimeFormatAPI用于处理特定语言环境的日期格式。constdate=newDate();//Sunday,January10,2021at9:02:29PMGMT+8newIntl.DateTimeFormat('en-US',{dateStyle:'full',timeStyle:'long'}).format(date)//21/1/10中国标准时间9:02:29.315pmnewIntl.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.matchAllmatchAll()返回包含所有匹配项的正则表达式公式的结果,返回值是一个不可重用的迭代器(不可重用的意思是读取后需要重新获取)。Node.jsv12.4.0及以上版本已经支持matchAll()方法。这种方法有一个局限性。如果设置的正则表达式不包含全局模式g,如果Node.jsv14.5.0及以后版本没有提供,则会抛出TypeError异常。//constregexp=RegExp('foo[a-z]*','g');//更正constregexp=RegExp('foo[a-z]*');//错误,没有全局模式conststr='tablefootball,foosball,fo';constmatches=str.matchAll(regexp);//TypeError:String.prototype.matchAllcalledwithanon-globalRegExpargumentfor(constitemofmatches){console.log(item);}参考:ES2020-features-String-prototype-matchAll-throws-on-non-global-regex[5]AsyncLocalStorageNode.jsAsyncHooks模块提供了一个API来跟踪Node.js程序中异步资源的声明周期。在最新的v14.xLTS版本中,新增了一个AsyncLocalStorage类,方便实现Context本地存储,在异步调用之间共享数据,对于实现日志链接跟踪场景很有用。下面是一个HTTP请求的简单示例,模拟异步处理,并在日志输出中跟踪存储的id。consthttp=require('http');const{AsyncLocalStorage}=require('async_hooks');constasyncLocalStorage=newAsyncLocalStorage();functionlogWithId(msg){constid=asyncLocalStorage.getStore();console.log(`${id!==undefined?id:'-'}:`,msg);}letidSeq=0;http.createServer((req,res)=>{asyncLocalStorage.run(idSeq++,()=>{logWithId('start');setImmediate(()=>{logWithId('processing...');setTimeout(()=>{logWithId('finish');res.end();},2000)});});})。听(8080);下面是运行结果。我是在第一次打完后直接打了第二次。可以看到我们保存的id信息连同我们的log一起成功打印出来了。image.png的便利性也会牺牲一些性能成本。关于AsyncLocalStorage的使用的详细介绍可以参考作者的另一篇文章《在Node.js中使用asynchooks模块的AsyncLocalStorage类处理请求上下文》。ES模块支持ES模块支持通常是一件好事。进一步规范了Node.js和浏览器的模块生态,使它们进一步收敛,避免进一步分裂。在当前的Node.jsv14.xLTS版本中已经移除了实验性支持,现在使用了no-use标志。它使用import和export关键字,有两种使用方式:使用.mjs扩展//caculator.mjsexportfunctionadd(a,b){returna+b;};//index.mjsimport{add}from'。/caculator.js';console.log(add(4,2));//6告诉Node.js将JavaScript代码视为Node.js默认将JavaScript代码视为ESModules的CommonJS规范,因此我们需要使用上面的扩展名.mjs声明它。此外,我们还可以在package.json文件中设置type字段为module或运行node时的标志--input-type=module告诉Node.js将JavaScript代码视为ES模块。//package.json{"name":"esm-project","type":"module",...}前端同学可能对以上ESModules的使用方法不陌生。顶级Await支持在异步函数之外使用await关键字。Node.jsv14.xLTS版本中移除了实验性支持,现在不再需要使用flag。importfetchfrom'node-fetch';constres=awaitfetch(url)也可以像调用函数一样动态引入模块。constmyModule=awaitimport('./my-module.js');对于异步资源,我们之前不得不在async函数中使用await,这对于一些需要在文件顶部实例化的资源可能效果不佳。现在有了顶层的await,我们可以很方便的在文件的最前面对这些异步资源做一些初始化操作。详细使用请参考作者在《Nodejsv14.3.0版本支持顶级Await和REPL增强功能》一文中的介绍。诊断报告(Diagnosticreport)诊断报告是Node.jsv14.xLTS提供的稳定功能。在某些情况下,会生成JSON格式的诊断报告,可用于开发、测试和生产环境。报告提供有价值的信息,包括:JavaScript和本机堆栈信息、堆统计信息、平台信息、资源使用情况等,以帮助用户快速跟踪问题。https://github.com/IBM/report-toolkit[6]是IBM开发的一个工具,用于简化报告工具的使用。下面是一个会导致服务内存泄漏的简单demo。consttotal=[];setInterval(function(){total.push(newArray(20*1024*1024));//内存占用大,不会释放},1000)最终生成的json报告诊断报告-toolkit工具结果可能如下。image.png的详细使用请看作者在《使用Node.js中的诊断报告快速跟踪问题》一文中的介绍。新版本的Stream包含了对Stream的一些改动,旨在提高StreamAPI的一致性,消除歧义并简化Node.js核心各部分的行为,例如:http.OutgoingMessage和stream.Writable类似于net.套接字行为与stream.Duplex完全相同一个显着的变化autoDestroy默认为true以使流在结束后始终调用_destroy参考:Node.jsversion14availablenow#Stream[7]Usingasynciteratorsusingasynciteratorswecan它是在Node.js中遍历事件、Stream或MongoDB返回数据非常有趣,虽然这不是Node.jsv14.x中提出的新功能。例如,event.on在Node.jsv12.16.0中才被支持。这些目前介绍的不多,这里简单介绍一下。在Events中使用Node.js在v12.16.0中添加了events.on(emitter,eventName)方法,该方法返回一个异步迭代器,用于迭代eventName事件。比如启动一个Node.js服务可以这样写。想知道它的原理可以看笔者下面提到的相关文章。import{createServerasserver}from'http';import{on}from'events';constee=on(server().listen(3000),'request');forawait(const[{url},res]ofee)if(url==='/hello')res.end('HelloNode.js!');elseres.end('OK!');在Stream中使用,以往我们可以通过on('data')监听事件,通过异步迭代器可以更简单的方式实现读取数据。asyncfunctionreadText(readable){letdata='';forawait(constchunkofreadable){data+=chunk;}returndata;}当前没有在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/文档/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]节点.js版本14现在可用#Stream:https://nodejs.medium.com/node-js-version-14-available-now-8170d384567e二维码关注。转载本文请联系Nodejs技术栈公众号。
