刷算法题遇到的新数据类型-—-BigInt

今天在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书籍资料

你可能感兴趣的:(刷算法题遇到的新数据类型-—-BigInt)