typhonjs-escomplex 代码可读性 可维护度探索
目前市面上的前端代码质量评分中的代码可维护度是大都是基于 typhonjs-escomplex
这个库扫描而来,但是这个库的官方文档并没有介绍相关指标数据的计算规则,不知道规则如何提升指标数据呢?所以本文对 typhonjs-escomplex
源码进行探索,探索其关键指标计算逻辑。
使用方式
使用方式很简单,引用后调用 analyze
方法传入需要检测的源代码即可。
import escomplexModule from 'typhonjs-escomplex-module';const ast = <some parsed AST>;const report = escomplexModule.analyze(ast);
analyze
的第二个参数可以传入相关配置项。
commonjs: true,
logicalor: true,
newmi: true,
但此库只支持js
文件的检测,对应像vue
这类文件无法检测,我们可以通过以下方式提取script
中的内容后进行检测。
const fs = require('fs');
const doc = fs.readFileSync('test.vue', 'utf-8');
const escomplex = require('typhonjs-escomplex');const srcs = doc.match(/(?<=<script>)[\s\S]*?(?=<\/script>)/g);
srcs.map((src) => {const report = escomplex.analyzeModule(src);
});
调用analyzeModule
返回的report
即是代码的相关检测指标数据。
需要注意的是此库已经5年没有更新了,由于这方面的库比较少加上本身的能力也比较强大,就算过去了5年没有维护的情况下现在的每周下载仍在1万+的数量。
VSCode插件
在VScode中可以安装插件 FE Doctor
或 AppWorks 的 Doctor
。
FE Doctor
这个插件可以帮你一键生成质量报告,且执行速度很快。
Doctor
可以在一次扫描中快速检测到应用程序和基础库代码中的各种安全漏洞和质量问题,你可以一键修复所有报告的问题,或者点击定位到源代码逐条来修复。
这两款插件底层都是基于typhonjs-escomplex
库扫码代码,只是UI交互有些不一样,有兴趣的可以安装试试看,看看你的代码能打多少分。
关键指标
typhonjs-escomplex
执行检测完返回的report
的数据如下,有所删减:
ModuleReport {lineEnd: 21,lineStart: 1,maintainability: 63.213,methods: [],aggregateAverage: MethodAverage {cyclomatic: 3,cyclomaticDensity: 30,halstead: HalsteadAverage {bugs: 0.053,difficulty: 8.182,effort: 1293.737,length: 36,time: 71.874,vocabulary: 21,volume: 158.123,operands: [Object],operators: [Object]},paramCount: 0,sloc: { logical: 10, physical: 21 }},}
需要重点关注的有以下几个数据,后续计算需要用到:
cyclomatic
: 代码圈复杂度halstead.difficulty
: 代码可读性halstead.effort
: 代码工作量(volume与difficulty的乘积)sloc.logical
: 源代码行数maintainability
: 代码可维护度
这里着重解释一下代码圈复杂度和代码可读性,代码可维护度是基于前面的几个值进一步计算而来。
代码圈复杂度
代码圈复杂度(cyclomatic)是一种度量代码复杂性的指标,它用于衡量程序中的单个方法或函数的复杂程度。
圈复杂度常用的计算公式如下:
圈复杂度 = E - N + 2P
- E表示程序中的边(一条边表示一条语句或条件语句等控制流转换)的数量;
- N表示程序中的节点(基本块,即一组顺序执行的语句)的数量;
- P表示程序中的组件(子程序或函数)的数量。
简单来说圈复杂度技术公式的含义:圈复杂度等于边的数量减去节点的数量,再加上两个控制流图的起点和终点。
较高的代码圈复杂度意味着代码逻辑较为复杂,难以理解和维护,也意味着代码中存在潜在的错误和缺陷。建议控制代码圈复杂度在一个可管理的范围内,一般上限为10-15之间,以提高代码的质量和可读性。
详细解析可参考维基百科:https://en.wikipedia.org/wiki/Cyclomatic_complexity
代码可读性
上面report
中的halstead
对象相关指标是指霍尔斯特德复杂度测量(Halstead complexity measures
),代码可读性是halstead
中的difficulty
字段。
霍尔斯特德复杂度测量是由霍尔斯特德在1977年提出的一种软件度量方法,是有关软件开发经验科学的论文中的一部分。 霍尔斯特德观察到软件度量应该要反映在不同编程语言中算法实现的方式,但又要独立于使用的平台及语言。这些度量要可以由静态代码中计算而得。
相关指标计算公式如下,详细解析可参考维基百科:https://en.wikipedia.org/wiki/Halstead_complexity_measures
:
从公式可以看出核心计算的基础主要是以下数值:
- η 1 \eta_1 η1 为不同运算子的个数。
- η 2 \eta_2 η2 为不同算子的个数。
- N 1 N_1 N1 为所有运算子合计出现的次数。
- N 2 N_2 N2 为所有算子合计出现的次数。
上述的运算子包括传统的运算子及保留字,算子包括变数及常数。依上述数值,可以计算以下的量测量: - 程式词汇数(Program vocabulary): η = η 1 + η 2 \eta = \eta _1+\eta_2 η=η1+η2
- 程式长度(Program length): N = N 1 + N 2 N=N_1+N_2 N=N1+N2。
程式词汇数可以理解为代码中的不同操作符和操作数的总数,较大数值意味着程序中使用了更多不同的操作符和操作数,可能需要更多的理解和维护工作。
程序长度是指程序中操作符和操作数的总数。突出程序的规模和复杂性,较长的程序长度意味可能需要更多的时间和资源来开发和维护。
知道其计算逻辑后我们的代码优化可以考虑以下几个方向:
-
识别并减少代码中不同操作符和操作数的数量。可以通过简化复杂表达式、删除不必要的变量或函数,以及使用更多的内置函数来实现。
-
删除冗余或重复的代码,重构代码、消除不必要代码带来的复杂性。使用高效的算法和数据结构,来减少程序长度。
-
复杂任务分解为更小、更易管理的函数或模块来实现。
-
使用更高级的控制结构和抽象代码,提高程序水平。使代码更易读、易于维护和理解。
代码可维护度
report
中的 maintainability
代表代码可维护度数值,1991年,保罗·奥曼(Paul Oman)和杰克·哈格迈斯特(Jack Hagemeister)在爱达荷大学设计了这个度量标准,该度量标准是根据其他三个度量标准的平均值在整个程序或模块级别上计算的,使用以下公式:
171 -
(3.42 * ln(mean effort)) -
(0.23 * ln(mean cyclomatic complexity)) -
(16.2 * ln(mean logical LOC))
最终取值范围从负无穷到171,较大的数值表示更高的可维护性水平。在他们的原始论文中,奥曼和哈格迈斯特确定65
是一个程序应被视为难以维护的阈值。
由此公式可以看出想要获取较大的数值需要让后面的减数足够小,那么我们就需要优化代码可读性,减小代码复杂度以及文件中代码的行数。
核心源码实现
接下来根据返回的report
查阅其源码找到maintainability
的计算代码如下:
function calculateMaintainabilityIndex(report, settings, averageCyclomatic, averageEffort, averageLoc) {report.maintainability = 171 - 3.42 * Math.log(averageEffort) - (0.23 * averageCyclomatic === 0 ? 0 : Math.log(averageCyclomatic)) - 16.2 * Math.log(averageLoc);if (report.maintainability > 171) {report.maintainability = 171;}if (settings.newmi) {report.maintainability = Math.max(0, report.maintainability * 100 / 171);}
}
calculateMaintainabilityIndex
函数用于计算代码的可维护度数据,核心计算和上面提到的公式一致。另外如果settings对象的newmi
属性为真,那么将maintainability
更新为其与171的比值的百分比,并确保其最小值为0且最大值为100,方便日常查看理解。
根据代码调用往上找到代码可读性halstead
相关属性的计算逻辑,和上面维基百科截图中的计算一致。
function calculateHalsteadMetrics(halstead) {halstead.length = halstead.operators.total + halstead.operands.total;if (halstead.length === 0) {halstead.reset();} else {halstead.vocabulary = halstead.operators.distinct + halstead.operands.distinct;halstead.difficulty = halstead.operators.distinct / 2 * (halstead.operands.distinct === 0 ? 1 : halstead.operands.total / halstead.operands.distinct);halstead.volume = halstead.length * (Math.log(halstead.vocabulary) / Math.log(2));halstead.effort = halstead.difficulty * halstead.volume;halstead.bugs = halstead.volume / 3000;halstead.time = halstead.effort / 18;}
}
从这段代码可以看出整个计算都是基于 halstead.operators
和 halstead.operands
这两个数值,为了方便理解,扫码以下代码断点查看所影响的具体内容。
export const bType = {0: '全部',1: '淘宝',2: '天猫',3: '京东'
}export const selectList = [{name: '今日',id: 0},{name: '近7日',id: 1},{name: '本月',id: 2}
]
以下是调试截图,可以看到 halstead.operators
最终的数据是代码中的操作符,而 halstead.operands
则是代码中的相关变量名称和对应的值。通过这两个数值可以看出要精简代码,简化复杂表达式、删除不必要的变量或函数才能提高相关数值。
总结
通过查阅代码圈复杂度和代码可读性的计算方法,并分析扫描库 typhonjs-escomplex
的源代码的实现逻辑,想要基于此扫码库提升代码质量以及相关指标数据可以从以下几个方面进行:
-
减少操作符和操作数的种类,尽量使用简单、常见的操作符和操作数,避免过于复杂或冗余的语言特性,以及使用更多的内置函数来实现。
-
模块化或组件复用,将代码分解为独立的模块或组件,每个模块负责特定的功能,这样可以减少重复的代码也会提示代码可维护度。
-
简化复杂逻辑,分解更小、更易管理的函数或模块来实现,避免过多的条件判断和嵌套循环,从而降低代码的复杂度。
-
代码重构:定期审查和重构代码,使其更加清晰、简洁,并符合良好的代码设计原则。
优化代码质量目标是减少代码的复杂性和冗余,提高代码的可读性和可维护性。但在实际优化过程中也需要权衡对代码的改造是否存在过大的风险和需要更多的投入回归测试。如果你对代码质量的要求高,那么这是一件需要长期要去做的事情,需要定期审查过往代码是否存在优化提升的空间。
看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~
专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)
相关文章:

typhonjs-escomplex 代码可读性 可维护度探索
目前市面上的前端代码质量评分中的代码可维护度是大都是基于 typhonjs-escomplex 这个库扫描而来,但是这个库的官方文档并没有介绍相关指标数据的计算规则,不知道规则如何提升指标数据呢?所以本文对 typhonjs-escomplex 源码进行探索…...
支持向量机基本原理,Libsvm工具箱详细介绍,基于支持向量机SVM的人脸朝向识别
目录 支持向量机SVM的详细原理 SVM的定义 SVM理论 Libsvm工具箱详解 简介 参数说明 易错及常见问题 完整代码和数据下载链接: 基于支持向量机SVM人脸朝向识别(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88527821 SVM应用实例, 基…...
密码破解工具的编写
预计更新 网络扫描工具的编写漏洞扫描工具的编写Web渗透测试工具的编写密码破解工具的编写漏洞利用工具的编写拒绝服务攻击工具的编写密码保护工具的编写情报收集工具的编写 密码破解工具是一种常见的安全工具,它可以通过不断尝试不同的密码组合来破解加密的数据或…...

BES2700H开发不完全手册
BES2700H开发不完全手册 是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?可加我微信hezkz17, 本群提供音频技术答疑服务,群赠送语音信号处理降噪算法,ANC AEC ENC EQ BF BES蓝牙耳机音频资料 1 成功编译 2 代码 3 开放文档...

OpenGL的学习之路-3
前面1、2介绍的都是glut编程 下面就进行opengl正是部分啦。 1.绘制点 #include <iostream> #include <GL/gl.h> #include <GL/glu.h> #include <GL/glut.h>void myMainWinDraw();int main(int argc,char** argv) {glutInit(&argc,argv);glutIni…...

Vue 小黑记事本组件版
渲染功能: 1.提供数据: 提供在公共的父组件 App.vue 2.通过父传子,将数据传递给TodoMain 3.利用 v-for渲染 添加功能: 1.收集表单数据 v-model 2.监听事件(回车点击都要添加) 3.子传父,讲…...
javascript如何清空数组?
可以使用以下方法清空JavaScript数组: 直接赋值为空数组 arr []; let arr [1, 2, 3, 4]; arr []; // 现在arr是空数组使用 splice() 方法删除所有元素 let arr [1, 2, 3, 4]; arr.splice(0, arr.length); // 现在arr是空数组使用 length 属性将数组截断 let ar…...
MySQL MHA高可用切换
MySQL MHA 1.什么是 MHA MHA(MasterHigh Availability)是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出现就是解决MySQL 单点的问题。 MySQL故障切换过程中,MHA能做到0-30秒内自动完成故障切换操作。 MHA能在…...

【Python】【应用】Python应用之一行命令搭建http、ftp服务器
🐚作者简介:花神庙码农(专注于Linux、WLAN、TCP/IP、Python等技术方向)🐳博客主页:花神庙码农 ,地址:https://blog.csdn.net/qxhgd🌐系列专栏:Python应用&…...

C++模拟实现——红黑树
一、介绍 红黑树也是对一般的搜索二叉树不能保证平衡的一个改进,和AVL树采用的思路不同,但同样需要旋转,其本质也是一颗平衡搜索二叉树,其节点有颜色的区分,并且被一些规则束缚,在这些规则下,能…...

java基础-数据类型
1、变量 变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。 内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。 因此,通过定义不同类型的变量,可以在内…...
设计数据库的时候会考虑哪些因素,怎样去建表?
在设计数据库时,通常会考虑以下因素: 数据的结构和关系:首先需要分析业务需求,了解需要存储的数据类型、数据之间的关系以及数据的组织结构。 数据的完整性和一致性:确保数据库中的数据完整性和一致性,例如…...

AI 绘画 | Stable Diffusion精确控制ControlNet扩展插件
ControlNet ControlNet是一个用于控制AI图像生成的插件,通过使用Conditional Generative Adversarial Networks(条件生成对抗网络)的技术来生成图像。它允许用户对生成的图像进行更精细的控制,从而在许多应用场景中非常有用,例如计算机视觉、艺术设计、虚拟现实等。 对于…...

青少年编程学习 等级考试 信奥赛NOI/蓝桥杯/NOC/GESP等比赛资料合集
一、博主愚见 在当今信息技术高速发展的时代,编程已经成为了一种必备的技能。随着社会对于科技人才的需求不断增加,青少年编程学习正逐渐成为一种趋势。为了更好地帮助青少年学习编程,提升他们的技能和素质,博主结合自身多年从事青…...

Linux 函数库
函数库: 我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢? 最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去…...

Java 入门基础题
目录 1.输出一个整数的每一位 2.判定素数 3.求最大值方法的重载 4.输出闰年 5.打印 X 图形 6.数字9 出现的次数 7.计算分数的值 8. 模拟登陆 9.使用函数求最大值 10.斐波那契数列 星光不负赶路人,加油铁子们!!! 1…...

块设备的工作模式
块设备的mknod 还是会创建在 /dev 路径下面,这一点和字符设备一样。/dev 路径下面是 devtmpfs 文件系统。这是块设备遇到的第一个文件系统。我们会为这个块设备文件,分配一个特殊的 inode,这一点和字符设备也是一样的。只不过字符设备走 S_IS…...

Spring核心
Spring Framework Spring的两个核心IOC控制反转IOC容器依赖注入DIIOC容器实现注解管理BeanBean对象定义Bean对象获取 AOP面向切面编程 添加依赖入门案例注解通过Spring创建Java bean对象 xml管理Bean案例main下创建bean.XMl文件 DI依赖注入案例创建Spring配置文件 bean-di.xml …...
ffmpeg命令行处理视频,学习记录
ffmpeg命令行处理视频 截取视频前5s ffmpeg -ss 00:00:00 -t 00:00:05 -i .\public\uploads\20231109\116a292eccf8315f65d7166e794d1730.mp4 .\public\uploads\20231109\116a292eccf8315f65d7166e794d1731.mp4两视频合并为1个 ffmpeg -i F:\xuejiao\code\cms.openlai.com\p…...

Linux应用层点亮硬件的LED灯
一 应用层操作硬件的两种方法 应用层想要对底层硬件进行操控,通常可以通过两种方式: /dev/目录下的设备文件(设备节点);/sys/目录下设备的属性文件。 具体使用哪种方式需要根据不同功能类型设备进行选择,通…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...

idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...

实战设计模式之模板方法模式
概述 模板方法模式定义了一个操作中的算法骨架,并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤。简单来说,就是在一个方法中定义了要执行的步骤顺序或算法框架,但允许子类…...

前端开发者常用网站
Can I use网站:一个查询网页技术兼容性的网站 一个查询网页技术兼容性的网站Can I use:Can I use... Support tables for HTML5, CSS3, etc (查询浏览器对HTML5的支持情况) 权威网站:MDN JavaScript权威网站:JavaScript | MDN...