当前位置: 首页 > 后端技术 > Node.js

使用JSDoc提高代码可读性

时间:2023-04-03 19:00:33 Node.js

工作四年多了,基本上都是围绕着JavaScript来做的。我写了更多的代码,阅读了更多的代码。真心觉得写别人看不懂的代码不是能力。写出人人都能看懂的代码,才是真正的牛逼。众所周知,JavaScript是一种弱类型的脚本语言,这意味着这段代码的功能无法从编辑器中直观的看出,有些东西只有在代码实际运行后才能确定。因此,为了解决大型项目中JavaScript维护成本高的问题,我们团队前段时间开始使用TypeScript,但是这几年积累的代码并不代表所有的改动都能立刻完成,所以这种重构将是一个漫长的过程。在重构的同时,我们仍然需要继续维护原有的JavaScript项目,而JSDoc恰好是一个中间过渡方案,可以让我们降低JavaScript项目的维护难度,并以注释的形式提高可读性。功能我用的是vscode编辑器,内置了对jsdoc的各种支持,同时还可以根据一些常量和语法推断出对应的类型,所以在编辑器里看效果很方便,所以下面所有的例子都是基于vscode的。首先,JSDoc对源码没有任何影响,所有内容都写在注释中。因此无需担心JSDoc会对您的程序产生任何负面影响。可以先看一个普通的JavaScript文件在编辑器中的显示效果:显然,编辑器无法确定这个函数的含义,因为可以添加两个任意类型的参数。所以小编会用TypeScript中经常出现的一个any关键字来标识任意类型来描述函数的参数和返回值。这种情况下,我们可以很方便的使用JSDoc来手动描述这个函数的作用:其实有些函数需要手动指定@return{TYPE}来判断函数的返回值类型,但是因为我们函数的功能是通过两个参数的相加和返回,所以小编推导出函数返回值的类型。对比上下两段代码,代码没有区别。可能有人会嗤之以鼻,认为代码已经足够清晰了,不需要额外的注释来解释。这种盲目的自信,通常在接手了别人更差的代码后就破灭了,然后反省自己哪里做错了,需要维护这样的代码。或者放个稍微复杂一点的例子:一个看似清晰简洁的例子,其实并没有什么不妥_除了两个异步等待可以合并为一个_。确实,如果这段代码一直躺在项目中,不改变需求,那么这段代码可以说是完美存在的。如果这段代码一直由编写这段代码的作者维护,那么维护这段代码就没有风险。但是,如果哪天这个代码交出来了,就交由其他小伙伴维护了。那么他可能会有几个问题:getUserInfo的返回值是多少?createOrder的返回值是什么结构?notify中传入的两个变量是干什么用的?一些线索,查看前两个函数返回的对象的一些属性,_但仍然无法知道这些属性是什么类型_。如果要维护这样一段代码,需要占用大量的脑容量去记忆,这其实是一件性价比很低的事情。当这段代码转给第三者时,第三者需要经过一个完整的流程,一个一个的函数,一行代码去读,去背。如果你以此作为对程序的深入理解和对业务的熟练掌握,那我想我帮不了你。就像现在在超市结账时,没有一个柜员会以能记住N多种商品的价格为荣。扫码枪都能做到,何必占脑筋呢。基本用法上面说了,JSDoc是用注释写的一些特定格式的内容。JavaScript文件中的大部分标签都是块级的,即使用/**XXX*/定义,但如果你愿意,也可以将它们写在代码中。JSDoc提供了多种标签用于各种场景。但是并不是所有的都是常用的(而且使用了vscode之后,很多需要手动指定的标签都可以由小编代替你来完成了),常用的无非就是以下几种:@type标识变量type@param标识函数参数类型和描述@return标识函数的返回值类型和描述。可以在此处找到完整列表。块标签基本上使用上面三个标签后,大部分问题都可以解决。JSDoc在写的时候有特定的要求,比如该行也必须有这样的结构/**XXX*/,如果是/*XXX*/会被忽略。多行书写法较为常用。在vscode中,可以直接在函数上方输入/**,回车。编辑器会自动填写很多内容,包括参数类型、参数说明、功能说明的预留位置。使用TAB键可以快速切换。事实上,@type的使用频率相对于其他两个是非常低的,因为大多数情况下@type用于标识变量的类型。变量的来源基本上只有两种:1.基本类型赋值2.函数的返回值首先是第一个基本类型的赋值。基本上,vscode会帮你搞定,你不需要手动指定。至于另一个函数的返回值,如果我们在函数中加上@return,那么调用函数并获取返回值的变量类型也会被设置为@return对应的类型。type但是因为另外两个标签里面有类型相关的规范,所以还是拿@type来说明吧。首先,JSDoc支持所有基本类型,包括数字、字符串和布尔值。/**@type{number}*//**@type{string}*//**@type{boolean}*//**@type{RegExp}*///或函数/**@type{function}*///包含参数的函数/**@type{function(number,string)}*///对象结构参数/**@type{function({arg1:number,arg2:string})}*///一个包含参数和返回值的函数/**@type{function(number,string):boolean}*/在vscode中输入上面的注释,就可以轻松得到动态提示。当然,对于函数,还是推荐使用@param和@return来达到更好的效果。一些扩展复杂类型的例子大多是基于基本类型的描述,但在实际开发过程中,不会说只有这些基本类型供你使用。必须有大量复杂结构类型的变量、参数或返回值。关于函数参数,JSDoc中有两种描述复杂类型的方式:但这只能应用在@param中,复用性不高。如果同一个结构有多个定义,那么我们需要把这样的注解复制多份,显然不是一种优雅的写法。或者我们可以使用另外两个标签,@typedef和@property,这两个标签的格式与上面提到的类似,可以应用于所有需要指定类型的地方:使用@typedef定义的类型可以很容易地重用,在需要的地方,我们可以直接指定我们定义的类型。同样,这样的自定义类型可以直接应用于@return。param这是一个比较重要的标记,用来标记函数参数的相关信息。具体格式如下(切换到TypeScript后,一般会去掉类型定义,改用代码中的类型定义):/***@param{number}paramdescription*/functiontest(param){}//也可以和@type结合写(虽然很少这样写)/***@paramparamdescription*/functiontest(/**@typenumber*/param){}可选参数如果我们要表示一个参数是可选参数,如果可能的话,在参数名称上包裹一个[]。/***@param{number}[param]Description*/functiontest(param){}同事在文档中也提到了默认值的写法,其实如果你的可选参数在参数位置已经有默认值处理,那么就不用额外加[]来表示了,vscode会帮你标记。//文档中写的默认值/***@param{number}[param=123]description*/functiontest(param=123){}//其实使用vscode后可以简化为/***@paramparamdescription*/functiontest(param=123){}两者的效果是一样的,而且由于我们手动指定了一个基本类型的值,我们甚至可以省略类型的指定,简单地定义即可参数说明。return这个标签用来指定函数的返回值,用法和@param类型一样,基本上两者都会同时出现,和@param的区别是因为@return只会有一个,它不会像以前那样返回需要指定参数名称。/***@return{number}描述*/functiontest(){}Promise类型返回值处理这个时代Promise基本普及了,所以很多函数的返回值可能不是一个result,而是一个Promise。所以在vscode中,要使用基于Promise的@return,有两种写法://当函数返回一个Promise实例时,可以这样指定类型/***@return{Promise}*/functiontest(){returnnewPromise((res)=>{res(1)})}//使用异步函数定义时可以省略@return的声明asyncfunctiontest(){return1}//如果返回值是另一个如果定义了该类型的函数或变量,效果是一样的asyncfunctiontest(){returnreturnVal()}/**@return{string}*/functionreturnVal(){}回到我们原来的代码片段,修改它以添加JSDoc版本:/***@typedef{Object}UserInfo*@property{number}uiduserUID*@property{string}namenickname**@typedef{Object}Order*@property{number}orderIdorderID*@property{number}price订单价格*/asyncfunctionmain(){constuid=1constorders=awaitcreateOrder(uid)constuserInfo=awaitgetUserInfo(uid)awaitnotify(userInfo,orders)}/***获取用户信息*@param{number}uid用户UID*@return{Promise}*/asyncfunctiongetUserInfo(uid){}/***创建订单*@param{number}uid用户UID*@return{Promise;}*/asyncfunctioncreateOrder(uid){}/***sendnotification*@param{UserInfo}userInfo*@param{Order}orders*/asyncfunctionnotify(userInfo,orders){}实际上并没有添加一个几行文字,在转用TypeScript之前,使用JSDoc可以在一定程度上降低维护成本,尤其是使用vscode之后,手动写的注释其实不多,但是好处是维护者可以清楚的看到函数的作用和变量的类型。代码是文档。而且在做日常开发的时候,结合编辑器的自动补全和动态提示功能,一定能够提升开发体验。以上只是JSDoc中常用的几个标签。其实还有更多的功能没有提到。具体文档地址:jsdoc参考jsdoc|@returnjsdoc|@paramjsdoc|@typedefjsdoc|@财产