当前位置: 首页 > 科技观察

提高程序代码质量的七大JavaScript良好实践

时间:2023-03-18 00:09:17 科技观察

【.com快译】自2015年以来,随着ECMAScript6(简称ES6)的发布,每年都会发布新版本的ECMAScript规范。每次迭代都会为该语言添加新功能、新语法和新的质量改进。为此,大多数浏览器和Node.js中的JavaScript引擎需要迎头赶上。甚至程序员的编程习惯和代码组织方式也需要与时俱进,以提高整体代码质量,方便后期维护。为了让你更容易写出更简洁、可读性更强的程序代码,本文将在梳理总结ECMAScript最新特性的基础上,为你提供提高JavaScript和Node.js代码质量的七大优秀实践。1.块作用域声明(BlockScopedDeclarations)自语言问世以来,JavaScript开发者一直使用var来声明变量。但是,如以下代码片段所示,使用关键字var创建的变量的作用域存在问题。varx=10if(true){varx=15//innerdeclarationoverridesdeclarationinparentscopeconsole.log(x)//prints15}console.log(x)//prints15由于定义的变量var不是块作用域的,如果在小作用域重新定义in,它们影响外部作用域中的值。但是,如果我们用两个新的关键字let和const替换var,就可以避免这个缺陷(参见下面的代码片段)。lety=10if(true){lety=15//innerdeclarationisscopedwithintheifblockconsole.log(y)//prints15}console.log(y)//prints10当然const和let在语义上是不一样的,那些用const声明的变量,不能重新赋值在其范围内(如下面的代码片段所示)。然而,这并不意味着它们是不可变的,只是它们的引用不能改变。constx=[]x.push("Hello","World!")x//["Hello","World!"]x=[]//TypeError:Attemptedtoassigntoreadonlyproperty。2.箭头函数(ArrowFunctions)作为最近引入JavaScript的一个重要特性,箭头函数有很多优点。首先,它们使JavaScript函数看起来更清晰,更易于开发人员编写。letx=[1,2,3,4]x.map(val=>val*2)//[2,4,6,8]x.filter(val=>val%2==0)//[2,4]x.reduce((acc,val)=>acc+val,0)//10如上例所示,“=>”后面的函数用简洁的语法代替了传统的函数。如果函数体是单个表达式,范围括号{}和return关键字已经隐含了,所以不需要额外写。如果函数只有一个参数,则参数括号()已经隐含了,不需要再写。如果函数体中的表达式是一组字典,则必须用圆括号()括起来。箭头函数的另一个好处是,为了避免使用this关键字带来的不便,箭头函数不定义作用域,而是会存在于它的父作用域中。也就是说,箭头函数对此没有任何绑定。在箭头函数中,this的值与父作用域中的值相同。因此,箭头函数不能用作各种方法或构造函数。它们既不适用于apply、bind或call,也没有super的绑定。此外,箭头函数还有其他限制,例如缺少传统函数可访问的参数对象,以及函数体中缺少yield。可以说,箭头函数并不是标准函数的1:1替代品,而是为JavaScript添加了额外的功能集。3.可选链(OptionalChaining)让我们想象一个类似于人对象的深度嵌套的数据结构。业务应用程序需要访问对象的名字和姓氏。由此,我们可以编写如下JavaScript代码:将出现以下错误。person={age:42}person.name.first//TypeError:Cannotreadproperty'first'ofundefinedperson.name.last//TypeError:Cannotreadproperty'last'ofundefined对于这个,开发者往往需要使用如下代码来解决。显然,这些代码不仅冗长难写,而且可读性也较差。person&&person.name&&person.name.first//undefined而作为JavaScript的一个新特性,可选链的语法允许你访问更深层次的嵌套对象属性,而不必担心该属性是否实际存在。也就是说如果可选链在挖矿过程中遇到null或者undefined值,会通过短路(short-circuit)计算,返回undefined,不会报错。person?.name?.first//undefined如上代码所示,生成的代码简洁明了。4.Null-ish合并在引入null-ish合并运算符之前,JavaScript开发人员需要使用OR运算符--||当输入为空时回退到默认值。这样就会导致即使有合法但错误的值(falsyvalues),也会回落到默认值的情况。functionprint(val){returnval||'Missing'}print(undefined)//'Missing'print(null)//'Missing'print(0)//'Missing'print('')//'Missing'print(false)//'Missing'print(NaN)//'Missing'现在,JavaScript推出了一个空合并运算符--??。它保证仅当前面的表达式为null-ish时才会触发回退。值得注意的是,这里的null值指的是null或者undefined。functionprint(val){returnval??'Missing'}print(undefined)//'Missing'print(null)//'Missing'print(0)//0print('')//''print(false)//falseprint(NaN)//NaN这样,您可以确保您的程序将接受错误值作为合法输入而不会最终被回滚。5.逻辑赋值假设在给变量赋值之前需要先判断是否为空,那么下面的代码展示了基本逻辑:if(x===null||x==undefined){x=y}If如果你熟悉上面提到的短路计算是如何工作的,你可能会使用null-ish合并运算符,用下面更简洁的版本替换上面的三行代码。x??(x=y)//x=yifxisnullish,elsenoeffect从上面的代码可以看出,如果x是null-ish,我们可以使用null-ishmergeoperator的short-circuit函数来执行第二个部分(x=y)。这段代码虽然非常简洁,但不是很容易阅读或理解。我们可以使用以下代码根据逻辑null-ish分配来消除此类变通方法。x??=y//x=yifxisnullish,elsenoeffect类似的,JavaScript也引入了逻辑与赋值--&&=,逻辑或赋值--||=运算符。这些运算符只有在满足某些条件时才被赋值,否则它们没有任何作用。x||=y//x=yifxisfalsy,elsenoeffectx&&=y//x=yifxistruthy,elsenoeffect专家建议:如果你有Ruby编程经验,你会一眼认出||=和&&=运算符。毕竟Ruby没有假值的概念。6、命名捕获组(NamedCaptureGroups)你知道正则表达式中“捕获组”的概念吗?如下代码段所示,是匹配正则表达式括号中部分的字符串。letre=/(\d{4})-(\d{2})-(\d{2})/letresult=re.exec('Pidaythisyearfallson2021-03-14!')result[0]//'2020-03-14',thecompletematchresult[1]//'2020',thefirstcapturegroupresult[2]//'03',thesecondcapturegroupresult[3]//'14',thethirdcapturegroup很长一段时间以来,正则表达式已经可以支持named捕获组。这是一种通过引用名称而不是索引来捕获组的方法。目前,在ES9中,该功能已经由JavaScript实现。如下面的代码片段所示,结果对象包含一个嵌套的组对象,其中每个捕获组的值都可以映射到它的名称。letre=/(?\d{4})-(?\d{2})-(?\d{2})/letresult=re.exec('Pidaythisyearfallson2021-03-14!')result.groups.year//'2020',groupnamed'year'result.groups.month//'03',groupnamed'month'result.groups.day//'14',groupnamed'day'而且,新的API和JavaScript的解构分配功能也可以完美结合(见下面的代码片段)。letre=/(?\d{4})-(?\d{2})-(?\d{2})/letresult=re.exec('Pidaythisyearfallson2021-03-14!')let{year,month,day}=result.groupsyear//'2020'month//'03'day//'14'7.async和await我们都知道异步是JavaScript的一个强大特性。许多可能长时间运行或耗时的函数可以返回Promise而不会被阻止执行。consturl='https://the-one-api.dev/v2/book'letprom=fetch(url)prom//Promise{}//waitabitprom//Promise{:Response},ifnoerrors//orprom//Promise{:Errormessage},ifanyerror在上面的代码片段中,对fetch的调用返回一个状态为“pending”的Promise。当API返回响应时,它将转换为“已完成”状态。在Promises中,您可以执行以下操作以通过API调用将响应解析为JSON。consturl='https://the-one-api.dev/v2/book'letprom=fetch(url)prom//Promise{:Response}.then(res=>res.json()).then(json=>console.log(json))//printsresponse,ifnoerrors.catch(err=>console.log(err))//printserrormessage,ifanyerror2017年,JavaScript引入了两个新的关键字async和await,来使得处理Promises的使用更轻松、更顺畅。当然,它们并不是Promises的替代品,只是在Promises概念之上的语法增强。此外,与其让所有代码都出现在一系列“then”函数中,await更像是同步JavaScript。你可以使用try...catch和await而不是直接使用Promise的catch函数来处理错误。下面是具有等效效果的await代码。consturl='https://the-one-api.dev/v2/book'letres=awaitfetch(url)//Promise{:Response}-await->Responsetry{letjson=awaitres.json()console.log(json)//printsresponse,ifnoerrors}catch(err){console.log(err)//printserrormessage,ifanyerror}当然,async关键字也有“硬币的另一面”,它封装了任何待处理的数据发送到Promise中。下面是一段程序代码,旨在通过异步函数将多个数字相加。实际上,您的代码可能比这更复杂。asyncfunctionsum(...nums){returnnums.reduce((agg,val)=>agg+val,0)}sum(1,2,3)//Promise{:6}.then(res=>console.log(res)//prints6letres=awaitsum(1,2,3)//Promise{:6}-await->6console.log(res)//prints6总结如你所见,JavaScript将为它的语言添加新的特性,希望我们上面介绍的7个关于代码质量的良好实践,能在日常编程中对你有所帮助。原标题:提高代码质量的7个JavaScript最佳实践,作者:DhruvBhanushali【译稿】,合作网站转载请注明原译者和出处为.com】