Skip to content

数值的扩展

ES6 对数值类型做了多项增强,包括新的字面量语法、Number 对象新方法、Math 对象扩展等。

数值字面量#

二进制和八进制#

ES6 新增了二进制(0b)和八进制(0o)字面量前缀:

// 二进制(Binary)
const bin = 0b1010
console.log(bin) // 10
// 八进制(Octal)
const oct = 0o17
console.log(oct) // 15
// 十六进制(已有)
const hex = 0xff
console.log(hex) // 255
// 转换为二进制/八进制字符串
;(10).toString(2) // '1010'
;(15).toString(8) // '17'
;(255).toString(16) // 'ff'

实际应用:

// 位掩码更直观
const READ = 0b0001 // 1
const WRITE = 0b0010 // 2
const EXECUTE = 0b0100 // 4
const permission = READ | WRITE // 0b0011 = 3
// 检查权限
const hasRead = (permission & READ) !== 0 // true
const hasExecute = (permission & EXECUTE) !== 0 // false
// 文件权限(类似 Linux)
const FILE_PERMISSION = 0o755 // rwxr-xr-x

数值分隔符#

ES2021 允许用下划线 _ 分隔数字,提高可读性:

const billion = 1_000_000_000
const bytes = 0xff_ff_ff_ff
const bits = 0b1010_0001_1000_0101
// 小数也可以
const pi = 3.141_592_653
// 科学计数法
const avogadro = 6.022_140_76e23
// 不能放在特殊位置
// const bad = _1000; // 错误
// const bad = 1000_; // 错误
// const bad = 1__000; // 错误
// const bad = 1._5; // 错误

Number 新属性#

Number.EPSILON#

表示 1 与大于 1 的最小浮点数之差:

Number.EPSILON // 2.220446049250313e-16
// 用于浮点数比较
function equal(a, b) {
return Math.abs(a - b) < Number.EPSILON
}
0.1 + 0.2 === 0.3 // false
equal(0.1 + 0.2, 0.3) // true

Number.MAX_SAFE_INTEGER / MIN_SAFE_INTEGER#

安全整数范围:

Number.MAX_SAFE_INTEGER // 9007199254740991 (2^53 - 1)
Number.MIN_SAFE_INTEGER // -9007199254740991
// 超出范围会丢失精度
9007199254740992 === 9007199254740993 // true(危险!)

Number.isFinite()#

判断是否为有限数(不会进行类型转换):

// 全局 isFinite 会转换类型
isFinite('25') // true
isFinite(null) // true
// Number.isFinite 不转换
Number.isFinite('25') // false
Number.isFinite(null) // false
Number.isFinite(25) // true
Number.isFinite(Infinity) // false
Number.isFinite(NaN) // false

Number.isNaN()#

判断是否为 NaN(不会进行类型转换):

// 全局 isNaN 会转换类型
isNaN('NaN') // true(字符串转换后是NaN)
isNaN(undefined) // true
// Number.isNaN 不转换
Number.isNaN('NaN') // false
Number.isNaN(undefined) // false
Number.isNaN(NaN) // true

Number.isInteger()#

判断是否为整数:

Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false
Number.isInteger('25') // false
Number.isInteger(null) // false

🔶 注意精度问题:

Number.isInteger(3.0000000000000002) // true(精度丢失)

Number.isSafeInteger()#

判断是否在安全整数范围内:

Number.isSafeInteger(3) // true
Number.isSafeInteger(9007199254740991) // true
Number.isSafeInteger(9007199254740992) // false
// 实际应用:验证计算结果
function safeAdd(a, b) {
if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b)) {
throw new Error('数值超出安全范围')
}
const result = a + b
if (!Number.isSafeInteger(result)) {
throw new Error('计算结果超出安全范围')
}
return result
}

Number.parseInt() / parseFloat()#

从全局移植到 Number 对象,行为完全一致:

Number.parseInt('12.34') // 12
Number.parseFloat('12.34') // 12.34
// 与全局方法相同
Number.parseInt === parseInt // true
Number.parseFloat === parseFloat // true

Math 扩展#

Math.trunc()#

去除小数部分,只保留整数:

Math.trunc(4.9) // 4
Math.trunc(-4.9) // -4
Math.trunc(0.123) // 0
// 与其他取整方法对比
const n = -4.7
Math.trunc(n) // -4(直接截断)
Math.floor(n) // -5(向下取整)
Math.ceil(n) // -4(向上取整)
Math.round(n) // -5(四舍五入)

Math.sign()#

判断数值的符号:

Math.sign(5) // 1
Math.sign(-5) // -1
Math.sign(0) // 0
Math.sign(-0) // -0
Math.sign(NaN) // NaN

实际应用:

function compareNumbers(a, b) {
const sign = Math.sign(a - b)
if (sign === 1) return 'a > b'
if (sign === -1) return 'a < b'
return 'a = b'
}

Math.cbrt()#

计算立方根:

Math.cbrt(8) // 2
Math.cbrt(-8) // -2
Math.cbrt(27) // 3

Math.clz32()#

返回 32 位整数前导零的个数:

Math.clz32(1) // 31(0b00000000000000000000000000000001)
Math.clz32(2) // 30(0b00000000000000000000000000000010)
Math.clz32(0) // 32
// 计算最高位
function highestBit(n) {
return 31 - Math.clz32(n)
}
highestBit(8) // 3(2^3 = 8)

Math.imul()#

32 位整数乘法:

// 普通乘法可能溢出
0x7fffffff * 0x7fffffff // 4611686014132420600(不精确)
// imul 返回正确的 32 位结果
Math.imul(0x7fffffff, 0x7fffffff) // 1

Math.fround()#

返回最接近的 32 位单精度浮点数:

Math.fround(1.337) // 1.3370000123977661
Math.fround(1.5) // 1.5(正好可以表示)
// 用于模拟 32 位浮点运算

Math.hypot()#

计算所有参数的平方和的平方根:

// 勾股定理
Math.hypot(3, 4) // 5
// 三维距离
Math.hypot(3, 4, 5) // 7.0710678118654755
// 实际应用:计算两点距离
function distance(p1, p2) {
return Math.hypot(p2.x - p1.x, p2.y - p1.y)
}
distance({ x: 0, y: 0 }, { x: 3, y: 4 }) // 5

对数方法#

// 以 e 为底
Math.expm1(1) // e^1 - 1 = 1.718...
Math.log1p(Math.E - 1) // ln(e) = 1
// 以 10 为底
Math.log10(100) // 2
// 以 2 为底
Math.log2(8) // 3
// 实际应用:计算数字位数
function digitCount(n) {
return Math.floor(Math.log10(Math.abs(n))) + 1
}
digitCount(12345) // 5

双曲函数#

Math.sinh(x) // 双曲正弦
Math.cosh(x) // 双曲余弦
Math.tanh(x) // 双曲正切
Math.asinh(x) // 反双曲正弦
Math.acosh(x) // 反双曲余弦
Math.atanh(x) // 反双曲正切

实战应用#

金额计算#

// 避免浮点数精度问题,转为整数计算
function add(a, b) {
const factor = 100
return (Math.round(a * factor) + Math.round(b * factor)) / factor
}
add(0.1, 0.2) // 0.3
// 更通用的方案
function safeCalculate(a, b, operation) {
const aStr = String(a)
const bStr = String(b)
const aDecimals = (aStr.split('.')[1] || '').length
const bDecimals = (bStr.split('.')[1] || '').length
const factor = Math.pow(10, Math.max(aDecimals, bDecimals))
const aInt = Math.round(a * factor)
const bInt = Math.round(b * factor)
switch (operation) {
case '+':
return (aInt + bInt) / factor
case '-':
return (aInt - bInt) / factor
case '*':
return (aInt * bInt) / (factor * factor)
case '/':
return aInt / bInt
default:
throw new Error('Unknown operation')
}
}

数字格式化#

// 千分位分隔
function formatNumber(num) {
return num.toLocaleString('zh-CN')
}
formatNumber(1234567.89) // '1,234,567.89'
// 保留指定小数位
function toFixed(num, decimals) {
const factor = Math.pow(10, decimals)
return Math.round(num * factor) / factor
}
toFixed(3.14159, 2) // 3.14
// 百分比
function toPercent(num, decimals = 2) {
return (num * 100).toFixed(decimals) + '%'
}
toPercent(0.1234) // '12.34%'

范围限制#

function clamp(value, min, max) {
return Math.min(Math.max(value, min), max)
}
clamp(5, 0, 10) // 5
clamp(-5, 0, 10) // 0
clamp(15, 0, 10) // 10
// 实际应用:滑块值、音量控制等

随机数#

// 指定范围的整数
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
// 随机数组元素
function randomItem(arr) {
return arr[randomInt(0, arr.length - 1)]
}
// 生成随机 ID
function randomId(length = 8) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
return Array.from({ length }, () => randomItem(chars.split(''))).join('')
}

小结#

类别新增内容
字面量0b(二进制)、0o(八进制)、_(分隔符)
Number 属性EPSILON、MAX_SAFE_INTEGER、MIN_SAFE_INTEGER
Number 方法isFinite、isNaN、isInteger、isSafeInteger、parseInt、parseFloat
Math 方法trunc、sign、cbrt、clz32、imul、fround、hypot、expm1、log1p、log10、log2

这些扩展让数值处理更加精确和便捷,特别是在处理精度敏感的金融计算时,要格外注意浮点数的精度问题。