伸手请直接跳转【方法合集】~这次我们就来聊聊数字处理的一些问题。项目中对数字的处理一定是不可避免的。毕竟数据是由数字组成的(雾),所以我们可以通过适当封装常见的数字处理场景来有效简化代码。下面是从简单到复杂的顺序介绍几个。将任意值转换为有效数字在某些场合下,我们可能会得到一些类型不安全的值,需要将其视为数字。这种情况在编写库时尤其常见。当我们直接使用parseFloat进行转换时,当数字不合法时会得到NaN,这会导致后面的操作全部变成NaN,所以我们需要将其返回0,以保证后面的操作正常。functiontoNumber(value:unknown){constnumber=parseFloat(valueasstring)returnNumber.isNaN(number)?0:number}将数值限制在一个范围这个场景和上面很相似,同样是为了保证数值的正确性,需要将数值限制在一个特定的范围内,针对数值的数值。functionboundRange(number:number|string,min:number,max:number){returnMath.max(min,Math.min(max,toNumber(number)))}这两个场景处理起来很简单,针对简化多次出现的复杂代码。把个位数改成两位数太简单了,就不多说了,在处理日期和时间的时候经常会出现这样的需求。functiondoubleDigits(number:number){返回数字<10?`0${number}`:number.toString()}将数字格式化成三位数字换句话说,通常将数字分成三位数字分隔符号通常用于提高一些大数字的可读性,或者显示金额:functionsegmentNumber(number:number|string,separator=',',segment=3){if(typeofnumber!=='number'){number=parseFloat(number)}if(Number.isNaN(number))return'0'let[integer,decimal]=String(number).split('.')constformatRegExp=newRegExp(`(\\d+)(\\d{${segment}})`)while(formatRegExp.test(integer)){integer=integer.replace(formatRegExp,`$1${separator}$2`)}decimal=decimal?`.${decimal}`:''return`${integer}${decimal}`}将数字保留为特定位数的小数保留特定位数是一项技术任务,因为存在问题js中小数点精度的损失。我们先来看一个例子:当我们要将十进制数17.275四舍五入到两位数时,学过小学数学的人应该知道结果是17.28,我们先用js中最常用的方法toFixed:17.275.toFixed(2)jsengine小学数学没学好,这个就是小数精度的损失,具体原理这里就不展开了,不懂的赶紧找相关资料。通过下面的代码,我们其实可以发现toFixed的性能和Math.round是差不多的(猜测底层实现是一样的,未验证):Math.round(17.275*10**2)一样.5是直接丢弃而不是进一步的一个。后来出现了一些奇怪的处理方式,比如在目标位数的基础上将数字扩大10倍再缩小10倍,然后四舍五入避免精度损失:parseFloat(`${Math.round(17.275*10**3/10)/10**2}`)看起来很正常,但不幸的是,这仅适用于数字17.275。我们把原来的数改成1.3335,把目标数改成3,问题又出现了:其实只要用Math.为什么说“直接”,因为其实我们可以通过一些方式对数字进行处理,然后用这些方法对数字进行处理,这样就可以达到间接处理的效果,从而避免精度的损失。其实这种方法很粗鲁。我们都知道精度的损失无非就是一个非常小的小数,所以在0.5的分界处,四舍五入或者四舍五入的判断就会因为这个非常小的小数而变得不准确。我们只需要在保留的目标数字的下一个数字上。一旦发现这个数字是5,我们就直接改成6。其他情况,我们就把这个数字后面的部分剪掉。那么这个小数就不会影响四舍五入或者四舍五入的判断了。我们来看代码:functiontoFixed(number:number,decimal:number){if(decimal===0)returnMath.round(number)letsnum=String(number)constpointPos=snum.indexOf('.')if(pointPos===-1)返回数字constnums=snum.replace('.','').split('')consttargetPos=pointPos+decimalconstdatum=nums[targetPos]if(!datum)返回数字if(snum.charAt(targetPos+1)==='5'){snum=snum.substring(0,targetPos+1)+'6'}else{snum=snum.substring(0,targetPos+2)}returnparseFloat(Number(snum).toFixed(decimal))}方法集合/***将任意值转换为数字,NaN将被处理为0*@paramvalue-待转换的值*/导出函数toNumber(value:unknown){constnumber=parseFloat(valueasstring)returnNumber.isNaN(number)?0:number}/***小于10的整数N变成'0N'字符串,方法不会检查输入参数*@paramnumber-要处理的整数*/ex端口函数doubleDigits(number:number){returnnumber<10?`0${number}`:number.toString()}/***将数字格式化为三维顺序*@paramnumber-要格式化的数字*@paramsegment-要分隔的位数,默认为3*@paramseparator-要分隔的符号,默认为','*/exportfunctionsegmentNumber(number:number|string,segment=3,separator=','):string{if(typeofnumber!=='number'){number=parseFloat(number)}if(Number.isNaN(number))return'0'let[integer,decimal]=String(number).split('.')constformatRegExp=newRegExp(`(\\d+)(\\d{${段}})`)while(formatRegExp.test(integer)){integer=integer.replace(formatRegExp,`$1${separator}$2`)}decimal=decimal?`.${decimal}`:''return`${integer}${decimal}`}/***实数保留一定的小数位数*@paramnumber-待处理实数*@paramdecimal-要保留的十进制数*/exportfunctiontoFixed(number:number,decimal:number){if(decimal===0)returnMath.round(number)letsnum=String(number)constpointPos=snum.indexOf('.')if(pointPos===-1)返回数字constnums=snum.replace('.','').split('')consttargetPos=pointPos+decimalconstdatum=nums[targetPos]if(!datum)返回数字if(snum.charAt(targetPos+1)==='5'){snum=snum.substring(0,targetPos+1)+'6'}else{snum=snum.substring(0,targetPos+2)}returnparseFloat(Number(snum).toFixed(decimal))}/***对一个实数进行一定的倍数展开,并保持一定的小数*@paramnumber-待处理的实数*@parammultiple-待展开的倍数*@paramdecimal-要保留的小数*/exportfunctionmultipleFixed(number:number,multiple:number,decimal:number){returntoFixed(number*multiple,decimal)}/***限制一个数字在指定范围内*@paramnumber-需要限制在范围内的数*@parammin-边界的最小值,包括这个值*@parammax-边界的最大值,包括这个值**@returns之后的数范围有限*/exportfunctionboundRange(number:number|string,min:number,max:number){returnMath.max(min,Math.min(max,parseFloat(numberasstring)))}上一篇传送门:【封装小窍门】列表处理函数的封装【封装小窍门】封装ofis系列方法最后推荐一下我个人的开源项目VexipUI-GitHub是一个比较完整的Vue3组件库,支持比较全面css变量,内置深色主题,全TypeScript和组合Api,特点是几乎所有组件的每一个属性都支持通过配置修改其默认值(传递一个对象),这应该是其他组件库目前没有的特性有~现招募小伙伴使用或参与维护开发本项目,本人实力很有限,文档、单元测试、服务端渲染支持、外设插件、用例等,只要大家有兴趣可以从各种入口点下载,非常欢迎大家参与。本期【封装小贴士】的内容源码包含在@vexip-ui/utils包下。GitHub,这个包也是单独发布的,不过暂时还没有Api文档,可能需要直接查看源码才能吃~
