Javascript 提案 BigInt 的一些坑

原创
2018/05/07 12:09
阅读数 82

昨天译了一篇文章:BigInt:JavaScript 中的任意精度整数。昨晚又抽空总结了一下 BigInt 的那些坑。

1. 定义形式

BigInt 使用数字字面量加 n 表示支持二进制、八进制、十六进制形式。

对于八进制,只支持新写法 0o064n,不支持旧的写法 0640

  1. 普通写法:

  
    
  
  
  
  1. 1n

  1. 十六进制:

  
    
  
  
  
  1. 0x6n

  2. 0X6n

  1. 八进制:

  
    
  
  
  
  1. 0o6n

  2. 0O6n

  3. 06n  // ❌SyntaxError

  1. 二进制:

  
    
  
  
  
  1. 0b10n

  2. 0B10n

  1. BigInt 不支持科学计数法形式:

  
    
  
  
  
  1. 1e25n // ❌ SyntaxError

2. 转换为字符串

当作为 key 时,所有值都会被转换为字符串,而 BigInt 转字符串时是没有后缀 n 的。

  
    
  
  
  
  1. String(12n) === "12"

因此:

  
    
  
  
  
  1. let obj = { };

  2. obj[32n] = 1;

  3. obj[32] === 1;

数组同理, array[5n] 等同于 array[5] 等同于 array["5"]

:这并不意味着 array[xxxn]array[xxx] 是一样的。因为 BigInt 可以超越 Number 的安全表示边界。

  
    
  
  
  
  1. let obj = {};

  2. obj[9007199254740993n] = "foo";

  3. obj[9007199254740993n] === "foo"; // ✅

  4. obj["9007199254740993"] === "foo"; // ✅

  5. obj[9007199254740993] === "foo"; // ❌

我们可以通过如下代码查一下原因:

  
    
  
  
  
  1. let obj = {};

  2. obj[9007199254740993n] = "foo";

  3. obj[9007199254740993] = "bar";

  4. Object.keys(obj);

  5. // ["9007199254740993", "9007199254740992"]

因为 String(9007199254740993)==="9007199254740992"

3. 零值处理

因为 BigInt 表示的是整数,所以只存在一个 0(无正零和负零之分)。

  
    
  
  
  
  1. Object.is(-0, 0) === false

  2. Object.is(-0n, 0n) === true

注意BigInt 中没有 +0n,具体原因见上。

4. 等值判断

BigInt 同值判定规则:

  1. 数组:

  
    
  
  
  
  1. [0].includes(0n) === false

  2. [0n].includes(0n) === true

  3. [0n].includes(+0) === false

  4. [0n].includes(-0) === false

  1. Set

  
    
  
  
  
  1. new Set([0]).has(0) === true

  2. new Set([0n]).has(0) === false

  3. new Set([0n]).has(0n) === true

  4. new Set([0]).has(0n) === false

  1. Map

  
    
  
  
  
  1. new Map([[0n, 42]]).has(0n) === true

  2. new Map([[0n, 42]]).has(0) === false

  3. new Map([[0, 42]]).has(0) === true

  4. new Map([[0, 42]]).has(0n) === false

由于 00n 不相等,所以在集合中,两者可以共存:

  
    
  
  
  
  1. let s = new Set([0, 0n]);

  2. s.size === 2;

  3. let m = new Map([[0, 42], [0n, 24]]);

  4. m.size === 2;

5. 与 Number 比较

BigIntNubmer 的不同。

BigInt 只有函数,没有构造器,因此不能使用 new 来创建 BigInt 的实例。

  
    
  
  
  
  1. new Number(0); // ✅

  2. new BigInt(0); // ❌

对某些特殊值的处理不同:

  1. 当没有参数时, Number 返回 0, BigInt 抛出 TypeError

  
    
  
  
  
  1. Number() // 0

  2. BigInt() // ❌ TypeError

  1. 当非数字时, Number 返回 NaN, BigInt 抛出 TypeError 或 SyntaxError

  
    
  
  
  
  1. Number(undefined) // NaN

  2. BigInt(undefined) // ❌ TypeError

  3. Number(null) // 0

  4. BigInt(null) // ❌ TypeError

  5. Number({}) // NaN

  6. BigInt({}) // ❌ SyntaxError

  7. Number("foo")  // NaN

  8. BigInt("foo") // ❌ SyntaxError

  1. 两者对于 -0(负零)的处理也不同

  
    
  
  
  
  1. Number(-0) === -0

  2. BigInt(-0) === 0n

  1. 两者都会把 true 转换为 1,把 false 转换为 0

  
    
  
  
  
  1. Number(true) === 1n

  2. Number(false) === 0n

  3. BigInt(true) === 1n

  4. BigInt(false) === 0n

  1. 对于浮点数, BigInt 抛出 RangeError 异常

  
    
  
  
  
  1. BigInt(4.00000001) // ❌ RangeError

  1. 对于 NaN 和正负无穷, BigInt 抛出 RangeError 异常

  
    
  
  
  
  1. BigInt(NaN) // ❌ RangeError

  2. BigInt(-Infinity) // ❌ RangeError

  3. BigInt(+Infinity) // ❌ RangeError

6. 类型转换

BigInt 不能隐式转换为 Number,所以在接受 Number 作为参数的运算中,将抛出 TypeError 异常

  
    
  
  
  
  1. isNaN(0n) // ❌TypeError

  2. isFinite(0n) // ❌TypeError

  3. Math.abs(-4n) // ❌TypeError

  4. "bar".substr(1n) // ❌TypeError

但是 Number 下面的函数可以使用:

  
    
  
  
  
  1. Number.isSafeInteger(0n) === false

  2. Number.isFinite(0n) === false

  3. Number.isNaN(0n) === false

  4. Number.parseInt(0n) === 0

7. 继续阅读


本文分享自微信公众号 - justjavac(justjavac-blog)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部