跳到主要内容

Symbol BigInt

Symbol 和 BigInt 类型都是es6及之后引入的,从现实中来看,相对的使用频率也不算特别高。

Symbol

Symbol 是 ES6 中引入的新数据类型,它表示一个唯一的常量,通过 Symbol 函数来创建对应的数据类型,创建时可以添加变量描述,该变量描述在传入时会被强行转换成字符串进行存储。

var a = Symbol('1')
var b = Symbol(1)
a.description === b.description // true
var c = Symbol({id: 1})
c.description // [object Object]
var _a = Symbol('1')
_a == a // false

有一个全局的symbol注册中心,用Symbol.for()创建的symbol会添加到这个注册中心,并用它的 description作为索引键。也就是说,如果你用Symbol.for()创建带有相同 description的两个 symbol,它们就是相等的。

const symbol1 = Symbol.for('test');
const symbol2 = Symbol.for('test');

symbol1 === symbol2; // true
console.log(symbol1); // 'Symbol(test)'

通常来说,除非你有非常好的理由,否则不应该使用全局注册中心,因为这会造成命名冲突。

symbol不会出现在JSON.stringify()的结果里,确切地说是JSON.stringify()会忽略symbol属性名和属性值:

const symbol = Symbol('test');
const obj = { [symbol]: 'test', test: symbol };

JSON.stringify(obj); // "{}"

基于上面的特性,Symbol 属性类型比较适合用于两类场景中:常量值和对象属性

避免常量值重复

假设有个 getValue 函数,根据传入的字符串参数 key 执行对应代码逻辑。代码如下所示:

function getValue(key) {
  switch(key){
    case 'A':
      ...
    ...
    case 'B':
...
  }
}
getValue('B');

这段代码对调用者而言非常不友好,因为代码中使用了魔术字符串(魔术字符串是指在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值),导致调用 getValue 函数时需要查看函数源码才能找到参数 key 的可选值。所以可以将参数 key 的值以常量的方式声明出来。

const KEY = {
  alibaba: 'A',
  baidu: 'B',
  ...
}
function getValue(key) {
  switch(key){
    case KEY.alibaba:
      ...
    ...
    case KEY.baidu:
      ...
  }
}
getValue(KEY.baidu);

但这样也并非完美,假设现在我们要在 KEY 常量中加入一个 key,根据对应的规则,很有可能会出现值重复的情况:

const KEY = {
alibaba: 'A',
baidu: 'B',
...
bytedance: 'B'
}

这显然会出现问题:

getValue(KEY.baidu) // 等同于 getValue(KEY.bytedance)

所以在这种场景下更适合使用 Symbol,我们不关心值本身,只关心值的唯一性。

const KEY = {
  alibaba: Symbol(),
  baidu: Symbol(),
  ...
  bytedance: Symbol()
}

PS:当然,这个其实也是相对的。大部分情况下,直接走字典也是可行的。比如:

const KEYMAP = {
ALIBABA: 'ALIBABA',
BAIDU: 'BAIDU',
...
}

避免对象属性覆盖

假设有这样一个函数 fn,需要对传入的对象参数添加一个临时属性 user,但可能该对象参数中已经有这个user属性了,如果直接赋值就会覆盖之前的值。此时就可以使用 Symbol 来避免这个问题。

创建一个 Symbol 数据类型的变量,然后将该变量作为对象参数的属性进行赋值和读取,这样就能避免覆盖的情况,示例代码如下:

function fn(o) { // {user: {id: xx, name: yy}}
  const s = Symbol()
  o[s] = 'new user obj'
  ...
}

获取目标target 的 symbol属性:

Object.getOwnPropertySymbols(target);

Symbol 唯一的用途就是标识对象属性解决属性冲突,表明对象支持的功能。如果不需要那么追求完美,似乎都用不上这个。

BigInt

BigInt 是一种内置对象,它提供了一种方法来表示大于 2^53 - 1 的整数。这原本是 Javascript中可以用 Number 表示的最大数字。BigInt 可以表示任意大的整数。

它在某些方面类似于 Number ,但是也有几个关键的不同点:不能用于 Math 对象中的方法;不能和任何 Number 实例混合运算,两者必须转换成同一种类型。在两种类型来回转换时要小心,因为 BigInt 变量在转换成 Number 变量时可能会丢失精度。

在实际项目中,出于精度考虑也可能会直接使用类似 BigDecimal 这个npm。

更多详情:BigInt