今天在LeetCode做的一道关于 “加一” 的算法题,题目如下
`给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。示例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。示例 2:
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。`
我的代码是这样的
const plusOne = function(digits) {
for(let i = digits.length - 1;i >= 0;i--){
if(digits[i] == 9){
digits[i] = 0;
} else {
digits[i]++;
return digits;
}
}
// 为9的情况
digits.unshift(1);
return digits;
};
后面在题解区看到一位大神的代码只有一行
const plusOne = (digits) => {
return (BigInt(digits.join('')) + 1n).toString().split('');
};
这个方法很巧妙,将数组转换成字符串,再转换成数字进行加一计算,最后再转回数组。我们平时将字符串强制转成数字用的是Number(), 这里的BigInt是什么呢?接下来我们详细了解下BigInt。
一、Number 类型的局限性
JavaScript 内部只有一种数字类型Number,也就是说,JavaScript 语言的底层根本没有整数,所有数字都是以IEEE-754标准格式64位浮点数形式储存,1与1.0是相同的。因为有些小数以二进制表示位数是无穷的。JavaScript会把超出53位之后的二进制舍弃,所以涉及小数的比较和运算要特别小心。 具体介绍可查看我的另外一篇文章:解决JS浮点数运算结果不精确的Bug
这意味着在JavaScript中,Number类型只能安全地表示-9007199254740991 (-(2^53-1)) 和9007199254740991(2^53-1)之间的整数,任何超出此范围的整数值都可能被自动四舍五入丢失精度。如下,可以看到 max + 1 与 max + 2 的值相同,这显然是不对的。
let max = Number.MAX_SAFE_INTEGER
console.log(max) // 9007199254740991
console.log(max + 1) // 9007199254740992
console.log(max + 2) // 9007199254740992
二、解决方案
在没有 BigInt 的时候,如果想要使用大整型,一些JS开发人员使用字符串类型表示大整数。 例如,Twitter API 在使用 JSON 进行响应时会向对象添加字符串版本的 ID。 还可以借助类似 BigInt 功能的第三方库。例如 bignumber.js。这有可能会影响 JavaScript 程序的效率,比如加载时间、解析时间、编译时间,以及运行时的效率。
三、BigInt介绍
BigInt数据类型的目的是比Number数据类型支持的范围更大的整数值。在对大整数执行数学运算时,以任意精度表示整数的能力尤为重要。使用BigInt,整数溢出将不再是问题。
四、定义BigInt
【4.1】在整数的末尾追加n
// 十进制
console.log(10n); // 10n
// 二进制
console.log(0b10n); // 2n
console.log(0B10n); // 2n
// 八进制
console.log(0o5n); // 5n
console.log(0O5n); // 5n
// 十六进制
console.log(0xFn); // 15n
console.log(0XFn); // 15n
【4.2】调用 BigInt() 构造函数
// 十进制
console.log(BigInt('10'); // 10n
// 二进制
console.log(BigInt('0b10')); // 2n
console.log(BigInt('0B10')); // 2n
// 八进制
console.log(BigInt('0o5')); // 5n
console.log(BigInt('0O5')); // 5n
// 十六进制
console.log(BigInt('0xF')); // 15n
console.log(BigInt('0XF')); // 15n
五、BigInt类型
既然 BigInt 是一个新的原始类型,那么它就可以使用 typeof 检测出自己的类。请记住,不能使用严格相等运算符将 BigInt 与常规数字进行比较,因为它们的类型不同:
typeof(10) // number
typeof(10n) // bigint
10n === 10 // false
10n == 10 // true
六、BigInt运算
BigInt 支持绝大部分常用运算符:+,-,,/,%,*。 除 >>>(无符号右移)之外的位运算符也可以支持:|,&,<<,>>,^,因为BigInt都是有符号的。为了兼容 asm.js ,BigInt不支持单目 (+
) 运算符。另外就是不能混合使用 BigInt与 Number 计算,否则会抛出异常
10n + 20n; // 30n
10n - 20n; // -10n
+10n; // TypeError: Cannot convert a BigInt value to a number
-10n; // -10n
10n * 20n; // 200n
20n / 10n; // 2n
23n % 10n; // 3n
10n ** 3n; // 1000n
const x = 10n;
++x; // 11n
--x; // 9n
10n + 10; // TypeError: Cannot mix BigInt and other types
// 混合运算需转换
BigInt(10) + 10n; // 20n
Number(10 ) + 10; // 20
七、BigInt与Number的比较
【7.1】BigInt只是函数,没有构造器,因此不能使用 new 来创建 BigInt 的实例
new Number(1) // √
new BigInt(1) // ×
【7.2】****当没有参数时,Number返回0,BigInt抛出TypeError
Number() // 0
BigInt() // TypeError
【7.3】当参数为非数字时,Number返回NaN,BigInt抛出TypeError或SyntaxError
Number(undefined) // NaN
BigInt(undefined) // TypeError
Number(null) // 0
BigInt(null) // TypeError
Number({}) // NaN
BigInt({}) // SyntaxError
Number("foo") // NaN
BigInt("foo") // SyntaxError
【7.4】两者对 -0 的处理也不同
Number(-0) === -0
BigInt(-0) === -0n
【7.4】对于布尔值,两者都会把 true 转换为 1
,把 false 转换为 0
Number(true) // 1
Number(false) // 0
BigInt(true) // 1n
BigInt(false) // 0n
【7.5】对于浮点数,BigInt抛出 RangeError 异常
BigInt(4.00000001) // RangeError
【7.6】****对于 NaN 和正负无穷,BigInt抛出RangeError异常
BigInt(NaN) // RangeError
BigInt(-Infinity) // RangeError
BigInt(+Infinity) // RangeError
【7.7】当使用 BigInt 时,带小数的运算会被取整
let a = 5 / 2; // 2.5
let b = 5n / 2n; // 2n
【7.8】Number 和 BigInt 可以进行比较,两者也可以混在一个数组内并排序。
1n < 2 // true
2n > 1 // true
2n > 2 // false
2n >= 2 // true
let arr = [3n, 4, 2, 1n, 0, -1n]
arr.sort() // [-1n, 0, 1n, 2, 3n, 4]
八、实例方法
【8.1】BigInt.prototype.toLocaleString() : **返回此数字的 language-sensitive 形式的字符串 查看案例
【8.2】BigInt.prototype.toString() : **返回以指定基数(base)表示指定数字的字符串 查看案例
【8.3】BigInt.prototype.valueOf(): **返回指定对象的基元值 查看案例
九、注意
1、由于在 Number 与 BigInt 之间进行转换会损失精度,因而建议仅在值可能大于2^53 时使用 BigInt 类型,并且不在两种类型之间进行相互转换。
2、对任何 BigInt 值使用 JSON.stringify() 都会引发 TypeError,因为默认情况下 BigInt 值不会在 JSON 中序列化。
3、如果你确定你的页面只跑在最新的 Chrome 中,那么现在就可以大胆的使用 BigInt 了,更优雅高效的处理大数据。若在其他浏览器中需要支持,可以使用 JSBI 这个库,日后甩掉它的姿势也十分优雅
文章每周持续更新,可以微信搜索「 前端大集锦 」第一时间阅读,回复【视频】【书籍】领取200G视频资料和30本PDF书籍资料