前端开发之处理数字的方法合集

处理数字是前端开发中常见的任务之一。通过使用格式化、转换和校验等方法,我们可以对数字进行各种操作,满足不同的业务需求。在实际开发中,根据具体需求选择适合的方法,并注意处理边界情况和错误处理,以确保数字的正确性和可靠性。

在前端中,最基本的处理数字的方式主要有以下几种:
  1. 将数字转换为字符串,可以使用toString()方法将数字转换为字符串,然后对字符串进行操作。
  2. 使用parseInt()方法将字符串转换为整数,或者使用parseFloat()方法将字符串转换为浮点数。
  3. 使用正则表达式进行数字匹配,可以使用正则表达式匹配字符串中的数字,然后对匹配到的数字进行操作。
  4. 使用Number对象的方法进行数字操作,Number对象提供了一些常用的数字方法,如toFixed()、toPrecision()等,可以对数字进行格式化、舍入等操作。
  5. 使用JavaScript的Math对象进行数学运算,Math对象提供了一些常用的数学方法,如abs()、round()、max()、min()等,可以进行数值计算和数值比较等操作。

除此之外,因为处理数字的方式比较灵活多样,需要根据具体的场景选择合适的方法。当涉及到数字处理时,有许多常见的场景和需求需要考虑。以下是一些常见的数字处理方案,可以帮助您更好地处理数字数据

数字展示和计算

数字转大写(财务金额版)

用于将数字金额转换为中文大写金额的功能。它接受一个数字作为输入参数,并返回对应的中文大写金额表示。通过调用该方法,可以方便地将数字金额转换为中文大写金额,满足财务报表、合同等场景对于金额的要求。例如,调用 convertCurrency(1234.56) 转换为人民币 “壹仟贰佰叁拾肆元伍角陆分”。

const convertCurrency = (money) => {
  const cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']; // 汉字的数字
  const cnIntRadice = ['', '拾', '佰', '仟']; // 基本单位
  const cnIntUnits = ['', '万', '亿', '兆']; // 对应整数部分扩展单位
  const cnDecUnits = ['角', '分', '毫', '厘']; // 对应小数部分单位
  const cnInteger = '整'; // 整数金额时后面跟的字符
  const cnIntLast = '元'; // 整型完以后的单位
  const maxNum = 9999999999999.99; // 最大处理的数字
  let integerNum; // 金额整数部分
  let decimalNum; // 金额小数部分
  let chineseStr = ''; // 输出的中文金额字符串
  let parts; // 分离金额后用的数组,预定义

  if (!money) {
    return;
  }
  money = parseFloat(money);
  if (money >= maxNum) {
    return;
  } // 超出最大处理数字

  if (money === 0) {
    chineseStr = cnNums[0] + cnIntLast + cnInteger; // 零 + 元 + 整
    return chineseStr;
  }

  money = money.toString(); // 转换为字符串

  if (money.indexOf('.') === -1) {
    // 只有整数的处理
    integerNum = money; // 金额整数部分
    decimalNum = ''; // 金额小数部分
  } else {
    // 有小数的处理
    parts = money.split('.');
    integerNum = parts[0]; // 整数部分 // 金额整数部分
    decimalNum = parts[1].substr(0, 4); // 最多处理4位小数 // 金额小数部分
  }

  // 获取整型部分转换
  if (parseInt(integerNum, 10) > 0) {
    let zeroCount = 0;
    const IntLen = integerNum.length; // 金额整数部分
    for (let i = 0; i < IntLen; i++) {
      const n = integerNum.substr(i, 1); // 金额整数部分
      const p = IntLen - i - 1;
      const q = p / 4;
      const m = p % 4;
      if (n === '0') {
        zeroCount++; // 位数直接加1
      } else {
        if (zeroCount > 0) {
          chineseStr += cnNums[0]; // 位数补零
        }
        zeroCount = 0; // 归零
        chineseStr += cnNums[Number(n)] + cnIntRadice[m];
      }

      if (m === 0 && zeroCount < 4) {
        chineseStr += cnIntUnits[q];
      }
    }
    chineseStr += cnIntLast; // 处理完整数部分,补上元
  }

  // 小数部分
  if (decimalNum !== '') {
    const decLen = decimalNum.length; // 金额小数部分
    for (let i = 0; i < decLen; i++) {
      const n = decimalNum.substr(i, 1);
      if (n !== '0') {
        chineseStr += cnNums[Number(n)] + cnDecUnits[i];
      }
    }
  }

  if (chineseStr === '') {
    // 最后处理一遍 整数部分
    chineseStr += cnNums[0] + cnIntLast + cnInteger; // 零 + 元 + 整
  } else if (decimalNum === '') {
    // 金额小数部分
    // 最后处理一遍 小数部分
    chineseStr += cnInteger; // 整
  }
  
  return chineseStr;
};

数字转换为中文数字

这个方法是将数字转换为中文数字的表示形式。它接受一个数字作为参数,然后将其转换为对应的中文数字表示。调用 numberTranToCN(12345) 将返回 “一万二千三百四十五”。

export const numberTranToCN = (num: number) => {
  const numChar = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
	const numUnit = ['', '十', '百', '千'];
  const arr = String(num).split('').reverse(); // 先转数字,再使用 split('') 方法将其拆分为单个字符,并通过 reverse() 方法反转顺序,以便从低位到高位进行处理
  
  let resStr = '';
  // 循环遍历每个字符,并根据其在数字中的位置,结合 numChar 和 numUnit 数组,生成对应的中文数字字符和单位。
  for (let i = 0; i < arr.length; i++) {
    const char = arr[i] === '0' ? numChar[0] : numChar[Number(arr[i])] + numUnit[i];
    resStr = char + resStr;
  }
  
  // 对生成的中文数字字符串进行一些替换操作,以去除多余的零和调整一些特殊情况,例如将 "一十" 替换为 "十"。
  const s = resStr
    .replace(/零+/g, '零')
    .replace(/零+$/, '')
    .replace(/一十(\w?)/, '十$1');
  return s;
};
价格千位符处理

用于对价格进行千位符处理的函数。例如,调用 priceInt(1234567.89) 将返回 “1,234,567.89”。

// 价格千位符处理
export const priceInt = (val) => {
  if (val) {
    val = val.toString();
    const arr = val.split('.');
    const price = arr[0].toString().replace(/\B(?=(\d{3})+$)/g, ',');
    return price + (arr[1] ? `.${arr[1]}` : '');
  }
  return val;
};
重写toFixed的方法;toFixed兼容方法

大家都知道toFixed方法可以用来保留两位小数,但是在一些特定的场景下toFixed无法给出我们想要的值。比如四舍五入时,toFixed(2) 对于 0.615 可能返回 0.61 而不是预期的 0.62。

/**
 * 重写toFixed的方法;toFixed兼容方法
 * @param number // 原数字
 * @param digit // 保留的位数,默认是2
 * @returns
   */

export const toFixedDigit = function (number: number, digit = 2) {
    // 如果小数位数超出了范围(大于20或小于0或不是正整数),则会抛出一个错误。
    if (digit > 20 || digit < 0 || !Number.isInteger(digit)) {
        throw new RangeError('toFixed() digits argument must be between 0 and 20');
    }
    //如果数字是NaN或大于等于10的21次方,则直接返回数字的字符串表示。
    // const number: any = this;
    if (isNaN(number) || number >= Math.pow(10, 21)) {
        return number.toString();
    }
    // 如果小数位数未指定或为0,则返回四舍五入后的整数。
    if (typeof digit == 'undefined' || digit == 0) {
        return Math.round(number).toString();
    }

    let result: string | number = number.toString(); // 原数值转字符串
    const arr = result.split('.'); // 从小数点分割

    // 仅有整数的情况,补0
    if (arr.length < 2) {
        result += '.';
        for (let i = 0; i < digit; i += 1) {
            result += '0';
        }
        return result;
    }
    const integer = arr[0]; // 整数部分
    const decimal = arr[1]; // 小数部分
    //如果小数位数与原始数字的小数位数相同,则直接返回原始结果。
    if (decimal.length == digit) {
        return result;
    }
    //如果小数位数少于原始数字的小数位数,则在结果后面添加零以达到指定的小数位数。
    if (decimal.length < digit) {
        for (let i = 0; i < digit - decimal.length; i += 1) {
            result += '0';
        }
        return result;
    }
    //如果小数位数多于原始数字的小数位数,则截取原始数字的小数部分并返回。
    result = integer + '.' + decimal.substring(0, digit);
    const last = decimal.substring(digit, digit + 1);// 四舍五入,转换为整数再处理,避免浮点数精度的损失if (parseInt(last, 10) >= 5) {//如果小数部分的下一位数字大于等于5,则进行四舍五入,将结果转换为整数,并再次调用toFixed函数以确保小数位数正确const x = Math.pow(10, digit);
​        result = (Math.round(parseFloat(result) * x) + 1) / x;
​        result = result.toFixed(digit);}return result;
};

用于实现两个数字的精确加法运算
function accAdd (arg1, arg2) {
var r1, r2, m;
// 尝试将第一个参数 arg1 转换为字符串,然后通过分割字符串获取小数部分,并计算小数部分的长度。如果出现错误(例如 arg1 不是数字或没有小数部分),则将 r1 设置为 0。
try{
r1 = arg1.toString().split(".")[1].length
} catch (e) {
r1 = 0
}
try{
r2 = arg2.toString().split(".")[1].length
} catch (e) {
r2 = 0
}
// 确定倍数:计算两个参数中小数位数较大者对应的倍数,即 10 的 Math.max(r1, r2) 次方。这个倍数用于将两个数字都扩大到整数,以便进行精确的加法运算。
m = Math.pow(10, Math.max(r1,r2))
// 进行加法运算并返回结果:将两个参数分别乘以 m,转换为整数后进行加法运算,然后再除以 m,得到精确加法后的结果。
return (parseInt(arg1*m, 10) + parseInt(arg2*m, 10)) / m
}
乘除计算,保留两位小数(mathjs)。

前端在处理计算时常会遇到精度丢失的问题,下面的方法解决了精度丢失,还要保留两位小数的数字,例如:0.75 * 421.9 = 316.425 ,计算机的值为316.424999…,后端需要的值是316.43(也就是316.425的四舍五入)。这个方法用到了mathjsdivide, bignumber, multiply

import { divide, bignumber, multiply } from 'mathjs';

/**
 * 乘除计算,保留两位小数
 * @param record:any // 数据来源
 * @param field: 'multiply' || 'divide' // 乘 | 除
 * @returns
   */
   export const feePrice = (record: any, field: string) => {
   if (field === 'multiply') {
       // record.feePrice = parseFloat(parseFloat((record.unitPrice * record.rentArea).toFixed(3)).toFixed(2));// 不满足1.5*218.21返回327.32
       // 解决精度丢失,还要保留两位小数的数字,例如:0.75 * 421.9 = 316.425 ,计算机的值为316.424999..,后端需要的值是316.43(也就是316.425的四舍五入)
       try {
           record.price = toFixedDigit(Number(multiply(bignumber(record.unitPrice), bignumber(record.rentArea))), 2);
         	 // mathjs中的计算方法['multiply', 'divide']中的值都需要是一个'bignumber'。并且计算方法返回的不是一个数字,需要对返回结果转数字处理。
          // toFixedDigit 是上面的"重写toFixed的方法",因为计算结果不一定是满足条件的两位小数
       } catch (error) {
           record.rice = 0;
       }
   } else if (field === 'divide') {
       // record.unitPrice = record.feePrice / record.rentArea || 0;
       // record.unitPrice = parseFloat((((record.feePrice * 100) / (record.rentArea * 100)) / 10000).toFixed(2)) || 0;
       try {
           record.price = toFixedDigit(Number(divide(bignumber(record.feePrice), bignumber(record.rentArea))), 2);
       } catch (err) {
           record.price = 0;
       }
   }
     return record;
   };

数字验证:

根据传入的验证规则对数字进行校验,并返回一个 Promise 对象。如果校验通过,Promise 对象将会被解析为一个空值;如果校验不通过,Promise 对象将会被拒绝,并返回一个包含错误信息的 Error 对象。

/**
 * 校验数字的验证规则函数
 * @param _:any // 包含验证规则的对象
 * @param value: number | string // 待验证的值
 * @param allowZero: boolean // 是否允许值为零
 * @returns
 **/

const checkNumberBase = (_, value, allowZero = true) => {
  const reg = /^\d*(\.\d{1,2})?$/;
  const { requiredSelf, messageSelf, required, min, max, equal } = _;

  if (typeof value !== 'number') {
    value = Number(value);
  }

  if (requiredSelf && !value) {
    return Promise.reject(new Error(messageSelf || '请输入'));
  }

  if (!value) {
    if (required) {
      return Promise.reject(new Error('请输入'));
    }
    return Promise.resolve();
  }

  if (Number.isNaN(value)) {
    return Promise.reject(new Error('请输入数字值'));
  }

  if (max && value > max) {
    return Promise.reject(new Error(`最大值为${max}`));
  }

  if (max && equal && value >= max) {
    return Promise.reject(new Error(`值需小于${max}`));
  }

  if (min && value < min) {
    return Promise.reject(new Error(`最小值为${min}`));
  }

  if (!allowZero && value === 0) {
    return Promise.reject(new Error('不能为0'));
  }

  if (value < 0) {
    return Promise.reject(new Error('不能为负数'));
  }

  if (!reg.test(value)) {
    return Promise.reject(new Error('最多为两位小数'));
  }

  return Promise.resolve();
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值