Skip to content

Latest commit

 

History

History
433 lines (326 loc) · 19.2 KB

README_ZH.md

File metadata and controls

433 lines (326 loc) · 19.2 KB

a-calc

npm version Static Badge Static Badge Static Badge npm downloads

特点与优势

🐤易用 将编码体验推到极致,极简api利于记忆

🚀快速 不断进行细节的优化,现在它的运算非常之快

💪强大 数字精准计算、数字格式化、完备的舍入规则、单位计算、强悍的类型提示

🐍灵活 灵活的api让你自由书写,想怎么写怎么写

🌽实用 诞生于实际业务覆盖了业务中的一切实用操作

支持的运算符 : + - * / % ** //

文档语言: 简体中文 | 英文

安装

npm install a-calc

引入

commonjs

const {calc, fmt} = require("a-calc")
// 或者
const {calc, fmt} = require("a-calc/cjs") // 这个写法是明确指定使用cjs版本,如果直接写 a-calc 不好使, 那就换成 a-calc/cjs 试试

es module

import {calc, fmt} from "a-calc"
// 或者
const {calc, fmt} from "a-calc/es"

浏览器端

<script src="https://unpkg.com/a-calc@latest/browser/index.js"></script> <!-- cdn链接 -->
<script src="node_modules/a-calc/browser/index.js"></script> <!-- npm安装之后也可以本地引入,二选一即可 -->
<script>
const {calc, fmt} = a_calc
</script>

快速开始

calc("0.1 + 0.2") // "0.3"

// 复杂一点的计算
calc("0.1 + 0.2 * 0.3 / 0.4 * (0.5 + 0.6)") // "0.265"

// 科学计数法的计算
calc("-2e2 + 3e+2") // "100"

// 带单位的计算
calc("0.1% + 0.2%", {_unit: true}) // "0.3%"

// 变量运算
calc("(a * (b + c))", {a: 1, b: 2, c: 3}) // "5"
calc("(a * (b + c))", [{a: 1, b: 2}, {c: 3}]) // "5"
calc("a + b", {a: "2$", b: "4$", _unit: true}) // "6$"
calc("a + b", {_fill_data: [{a: "2$"}, {b: "4$"}], _unit: true}) // "6$"

// 计算并格式化: 千分位
calc("a + b | ,", {a:324232421123, b: 234234242422321}) // "234,558,474,843,444"
// 计算并格式化: 分数
calc("2 * 3 | /") // "6/1"
// 计算并格式化:输出数字
calc("1 + 1 | !n") // 2

关于空格

默认情况下表达式中的空格不是必须的,除非你使用了space或者space-all模式或calc_lite函数,关于这两个模式的介绍在后面的章节有具体介绍,但是我推荐你始终在表达式中包含空格,这样看起来更清晰好看。

填充变量并计算

let a = 0.000001
let b = 888.789
calc("a + b", {a,b}) // "888.789001"

calc("a * (b + c) % d + 7.123", [
    {a: 1, b: 2},
    {c: 3, d: 4}
]) // "8.123"

// 复杂一点的
calc("1 + o.a / arr[0].d",{
    o: { a: 2 },
    arr: [{ d: 8 }]
}) // "1.25"

calc("a + b - c",[
    {a: 1},
    {b: 2, c: 3}
]) // "0"

带单位的计算

实际情况不总是那么理想, 也许我们不得不计算两个百分比数字, 幸好现在a-calc支持这些操作, 但是请注意,单位会从第一个携带单位的数字上取, 后面的单位会被忽略

// 要特别注意 _unit是必须的, 且不是默认开启的, 这是因为带单位的计算会额外做一些操作, 相比之下单纯的数字计算更快
calc("1 + 2%", {_unit: true}) // "3%""

calc("1.123$$$ + 2.88% | + =6", {_unit: true}) // "+4.003000$$$"

// [email protected]开始数组形式的填充数据也可以进行配置了,可以将配置对象作为数组的第一个参数或者最后一个参数,也只支持这两个位置
calc("a + b", [{a: "1%", b: "2%"}, {_unit: true}]) // "3%"

实际开发中你可能希望最终的结果不要自动携带单位,在 1.3.6 之后的版本可以通过格式化参数 !u 去除结果中的单位,或者 !n 直接输出数字。

计算并格式化

格式化支持如下功能:限制小数位数,保留正负号,百分比输出,科学计数法输出,千分位输出,并且他们是可以组合的,不过有个别的情况组合是不生效的,这个自己去试一下,组合情况太多,不在一一列举。

格式化列表:

  • >|>=|<|<=|=数字 表示限制小数位数,例: <=2 小数位数小于等于2 >3 小数位数必须大于3,这个等价于>=4
  • , 输出为千分位数字字符串
  • / 输出为分数
  • + 输出的正数带+
  • % 输出百分比数字,可以和限制小数组合使用
  • !e 输出为科学计数法
  • !n 输出为数字而不是数字字符串,n可以大写,1.3.6版本之后这个优先级为最高,任何其他的格式化参数无法影响该参数。
  • !u 从结果中去除单位
// 操作小数位数
calc("0.1 + 0.2 | =2") // "0.30"
calc("0.11111 + 0.11111 | <=4") // "0.2222"
calc("0.11 + 0.11 | <=4") // "0.22"
calc("0.1 + 0.2 | >= 5") // "0.30000"
calc("0.0000001+ 0.0000001 | >= 5") // "0.0000002"

// 保留正负号
calc("1 + 1 | +") // "+2"

// 千分位
calc("10000000 + 100000000 | ,") // "110,000,000"

// 分数
calc("0.025 + 0.2 | /") // "9/40"

// 百分比
calc("1 + 1 | %") // "200%"

// 科学计数法, 注意这个 e也可以大写
calc("1 + 1 | !e") // "2e+0"

// 同时指定小数位和千分位且保留正负号
calc("10000000 + 100000000 | +,=10") // "+110,000,000.0000000000"

四种舍入规则

舍入规则通过在格式化字符串的部分加入,他们的符号分别为:

  • ~- 去尾,默认的舍入规则
  • ~+ 进一
  • ~5 四舍五入
  • ~6 四舍六入,该舍入规则相较四舍五入更为精准,规则在舍入的后一位为5的时候有所不同,他会查看5后面的位置,如果后面的数字不为0那么会进一,如果后面的数字为0 ,那么会看5前面的数字是否为偶数,如果是则不进,不是则进
calc("0.11 + 0.22 | =1 ~+") // "0.4" 保留一位并进一
calc("0.55 | =1 ~5") // "0.6"
calc("0.65 | =1 ~6") // "0.6"

这个新加入的舍入规则似乎会让格式化的部分更加的长,但是实际情况不是这样,一般一个项目的舍入规则是固定的,所以舍入规则部分的格式化应该被封装在默认的格式化参数中,在实际使用的时候完全不需要书写这部分内容,参考下面的默认格式化 说明

只格式化

calc("0.1 | =2") // "0.10"
fmt("0.1 | =2") // "0.10"
// calc 具备 fmt 的功能, 但是fmt具备更好的语义

fmt("1000000 | ,") // "1,000,000"

配置版本号打印和库更新检测

你可以开启或关闭控制台打印当前库的版本号,也可以开启或关闭控制台提示当前是否有新的版本更新。

import { calc_util } from "a-calc"
calc_util.print_version(); // 在控制台打印版本
calc_util.check_update(); // 开启检测更新功能,如果有更新会在控制台提醒

高级技巧

错误处理

通常直接使用calc做计算要求输入的计算式是完全正确的, 默认 a-calc 不会帮你处理计算式的错误, 这个可以自己做过滤, 但在项目里我们可能不想做这件事情那么就需要额外的高级API, 在输入的计算式有误的时候静默捕获并给出一个合适的返回值

calc("1 + 2sd + d",{
    _fill_data: {d: 3}, // 从这里数据源对象要赋给 _fill_data, 该对象也可以是一个对象数组,此时取数据的时候是依次从数组的项里查找,找到第一个立刻停止
    _error: "-", // 计算式出错的时候返回 - 作为替代值
})

// 上面的写法可以简化一下
calc("1 + 2sd + d", {
    d: 8,
    _error: "-"
}) // 这种简化单纯是为了方便

默认格式化

在实际项目中可以利用默认格式化优化开发体验

calc("111111 + 11111 | ,",{_fmt: "=2"}) // "122,222.00" 很显然 , 和 =2 被组合起来了,且表达式中的格式化字符串优先级更高

怎样在项目中二次封装?

在实际的项目中核心的 calc 函数可能还不够极致的便捷,因此a-calc1.2.10 版本之后提供了一个内置的二次封装的函数calc_wrap,它本质是 calc 的扩展,所以它拥有所有前者的功能只是多了更多灵活的写法,和强大的类型推导。

注意这也许不是唯一正确的封装方式,我只是提供了这个功能而已,这里没有教条,你应该灵活应对你自己的场景。

我建议如果决定将 calc_wrap 引入项目,那么你可以将其重命名为 calc 这样可以少写几个字符,下面将展示一些灵活的写法和强大的类型推导。

// 注意这里将 calc_wrap 重命名为 calc, 因为如果你需要使用 calc_wrap 函数的时候,基本用不到核心的 calc 函数,那么有这个闲置好名字就应该拿来用
import { calc_wrap as calc } from "a-calc";

const state = {
    a: 1,
    b: 2,
    c: 3
};

// 当传入的参数是一个不含变量名的计算式将会直接返回计算结果
calc( "(1 + 2) * 3" ); // 返回类型: string

// 当传入的参数是一个疑似包含变量名的计算式且没有第二个数据源参数时,会返回一个等待传入数据源的函数,没错这个功能通过静态类型的推导做到了
calc( "(a + b) * c" ); // 返回类型: ( data: any ) => string
calc( "(a + b) * c" )( state ); // 返回类型: string

// 也许你希望先注入状态然后在输入表达式,这也是可以的
calc( state ); // 返回类型: ( expr: string | number ) => string
calc( state )( "(a + b) * c" ); // 返回类型: string

// 原本的用法自然也是支持的
calc( "a + b + c", state ); // 返回类型: string

// 你依然可以将配置与数据源混合在一起,这是非常方便的
calc( "a + b + c" )( { ...state, _error: 0 } ); // 返回类型: string | 0

不推荐的写法

a-calc 可以使用模板字符串写法, 但是我在实践中发现这种写法的可读性很糟糕,除非你真的有足够合理的理由,不然不建议使用模板字符串的写法。

calc(`${a} + ${b}`) // 这种写法不推荐
calc("a + b", {a,b}) // 推荐写法,因为更清晰

space与space-all模式

space 与 space-all 两个模式对代码的编写提出了更高的要求,space模式要求在计算式部分的每个单元之间严格插入空格,space-all 不仅要求计算式部分严格插入空格并且要求fmt格式化部分也要插入空格,该功能几乎最重要的作用是消除歧义,其次该功能可以小幅度提升性能。

calc("1+1", {_mode: "space"}) // 这个写法无法计算,因为缺少空格
calc("1 + 1", {_mode: "space"}) // 这个写法正确
calc("1 + (2 * 3)", {_mode: "space"}) // 这个写法也对,因为对括号做了特殊处理,括号可以挨着内部的数字或变量名也可以用空格隔开

calc("1 + ( 2 * 3 ) | =2 ,", {_mode: "space-all"}) //使用 space-all 模式之后=2与,之间也需要空格了,每个格式化单元之间都要有至少一个空格,空格可多不可少。

原始方法

你还可以使用 plus sub mul div 等方法来计算,虽然a-calc主要解决的问题就是这类方法编写不直观的问题,但是如果你的操作数只有两个且无需任何的格式化那么用这些方法能够带来一定的性能提升

import {plus, sub, mul, div, mod, pow, idiv} from "a-calc"
plus(1, 1) // 2
plus(1, 1, "string") // "2"

精简方法calc_lite

calc_lite是calc的精简版本,其功能不如calc那么全面,但是内部逻辑比calc更简单且性能比calc强一些(但提升有限),同样的也有一些缺点,书写体验不如 calc,而且不如 calc 那么强大,一些功能在 calc_lite 中不可用:该函数不支持单位运算,不支持模式指定,不支持类似 -(3 - - (-2)) 这样的计算式。

calc_lite 函数的算术式和格式化部分是分开传入的,并且所有的计算式内部单元都要用空格严格分割!该函数不是为了取代calc而存在的,他只是calc的逻辑精简版本,以牺牲一部分不常用的功能性和书写体验来换取有限的性能提升。

注意该函数不会抛出任何异常,默认出现错误的时候函数会返回 -

import {calc_lite} from "a-calc"

// 函数一共4个参数,分别为 calc_expr, fmt_expr, data, err_value = "-", 它们分别对应计算式、格式化字符串、填充数据、错误时返回值
// 这些参数只有第一个参数是必须传入的,其他的可以不传,如果要跳过传入那么可以传一个 null 或 undefined
calc_lite("a + b", null, {a: 0.1, b: 0.2}) // "0.3"
calc_lite("1 + 2 + b") // "-"
calc_lite("1", "=2") // "1.00"
calc_lite("( 1 + 2 ) * 3") // "9" 括号左右也要严格插入空格
calc_lite("1+1") // "-" 因为计算式的单元之间没有用空格严格分割导致计算错误

版本变更

  • 2.2.7
    • 更好的提示文档,在编写每个函数的时候你的ide都会弹出函数的说明和使用示例
    • calc_lite 函数参数传递优化 ,calc_lite现在的使用方式更加灵活了
  • 2.2.0
    • 推出更简洁更高性能的 calc_lite 函数
  • 2.1.0
    • 破坏性变更:所有的memo方法都被移除,原因是memo方法带来了更多的代码量,但是经过多场景测试它仅仅能在特定的场景带来大幅度的性能提升,在一些业务场景它几乎不会带来性能提升,由于解析器性能足够高,缓存逻辑所占用的运行时间常常抵消掉了原本省下来的解析时间,总之其带来的综合收益太低。
  • 2.0.0
    • 破坏性变更:_unit参数现在只支持boolean类型,原先的space取值现在移动到了_mode参数
    • 破坏性变更:以前格式化部分的字母大小写都可以,现在只能小写,例如: !u 不能写成 !U
    • 性能大幅提升,现在它是同类库最快的
    • 暴露高性能的函数方法,对于简单的算术式你可以选择使用简单的函数来调用,例如:plus(1, 1)
    • 添加了 _mode 模式配置
    • 现在第二个参数为数组的时候也可以进行配置了
    • 暴露了 plus sub mul div mod pow idiv 等原始方法和对应的memo版本
    • 支持了 // 取整运算符
    • ** 改为右结合与原生js语言规则保持一致
  • 1.3.9 解决格式化部分注入变量为0时导致的取整失败(问题反馈人:MangMax)
  • 1.3.8 解决由于 vite5.x 的升级导致的打包失败问题(问题反馈人:武建鹏)
  • 1.3.6
    • !n 格式化参数优先级调整为最高,任何其他格式化参数无法影响它。
    • 新增!u格式化参数,可以去除结果中的单位部分
    • 类型提示增强
  • 1.3.4
    • 解决四舍五入与四舍六入舍入错误的bug(bug提供者: nanarino)
  • 1.3.0
    • 破环性变更:调整版本号打印功能和检测更新功能的调用方式
    • 完善类型提示
    • 添加更多的单元测试
  • 1.2.30
    • 之前的版本默认会在控制打印版本号,现在它是可配置的,而且默认关闭
    • 提供了检测更新功能,开启之后如果有新版本会在控制台给出提示
  • 1.2.10
    • 删除vue集成示例,该库本身不于某个前端框架绑定,为避免误解,删除对应集成代码。
    • 增加 calc_wrap 功能,该函数是对核心函数 calc 的二次封装,可以拿来直接使用。
  • 1.2.6
    • 调整vue3集成代码,由于vue3的组件实例在开发环境和生成环境有所不同,所以生产环境无法获取state,但是开发环境可以获取。
  • 1.2.0
    • 很小的破坏性更新, 以前的-e-n 分别变成 !e!n
    • 文档更新
  • 1.1.0
    • 很小的破环性更新,以前的 \e 科学计数法输出,现在是 -e,其他没变
    • 增加了 -n 输出数字类型
    • 对于小数位的限制支持了 <> 符号
    • 修复了若干舍入格式化问题
    • 单元测试增加到107个
  • 1.0.25
    • 文档更新,简化 a-calc 集成到vue3的写法
  • 1.0.23
    • 文档更新,重新编写将a-calc集成到vue3的推荐写法
  • 1.0.22
    • 优化小数位舍入逻辑
  • 1.0.21
    • 完善导出的类型定义
  • 1.0.19
    • 修复 _error 为空字符串的时候可能无法捕获错误的问题
  • 1.0.14
    • 修复 ** 运算符优先级错误的问题。
    • 对于 <= 格式化时可能会有多余的0未去除的问题进行修复。
  • 1.0.12
    • 文档添加库体积说明
    • 修复表达式为空的时候加上 _error 参数依然异常的问题, 并添加相应的单元测试
  • 1.0.10
    • 更新文档
  • 1.0.6
    • 破坏性变更:所有暴露出的小驼峰命名全部改成了蛇形命名法例如原来的_fillData 现在变成了 _fill_data,原因是蛇形命名法更清晰。
    • 内部代码极大的简化,解析器几乎完全重写,带来更稳定的体验
    • 原先的设计就是calc函数具备所有fmt的功能,可是1.0.6之前的版本虽然符合这个设计,但calc和fmt是分别实现的,现在fmt只是calc的别名。
    • 支持新的运算符 **
    • 支持新的格式化字符 % 可以将数字输出成百分比
    • 支持新的格式化字符 \e,可以将数字格式化成科学计数法
    • 修复格式化字符串非法的时候可能造成死循环的问题
    • 解决1/0是 Infinity 的问题
    • 增加若干单元测试
    • 更详细的类型提示
    • 更新文档,添加vue3集成的示例代码
  • 0.0.80
    • 带来4种舍入规则,分别为:去尾、进一、四舍五入、四舍六入
    • 更多边界情况的检测
    • fmt允许不传入格式化字符串,这个特性允许你使用 fmt 来清除小数点后多余的0
  • 0.0.79
    • 更新文档
  • 0.0.78
    • 支持科学计数法的计算
    • 完整的单元测试
    • 更多边界情况的检测
  • 0.0.72
    • 支持单个数值的带单位写法, 例如 calc("1元", {_unit: true}) 或者 fmt("1元 | =2",{_unit: true})
    • 补充文档

性能对比

性能测试会有误差,在不同电脑的表现也会不同,最终数据仅作为不同库之间相对性能指标的参考。

[email protected] [email protected] [email protected] [email protected]
50000次复杂运算用时(ms) 423 3791 610 701
500000次复杂运算用时(ms) 3650 36948 5724 6764

注意

  • 不要对单个数字包裹括号

视频教程

待定

问题提交

反馈问题请携带错误案例和尽可能多的问题信息,请勿发一句极其抽象与概括的话当做问题反馈!你未提供的信息我不能通过预测获得。通常工作日一天内会发布新版修复问题。

Stargazers repo roster for @Autumn-one/a-calc