ChuChencheng / note

菜鸡零碎知识笔记
Creative Commons Zero v1.0 Universal
3 stars 0 forks source link

ES6 Symbol #22

Open ChuChencheng opened 4 years ago

ChuChencheng commented 4 years ago

新的原始类型 Symbol

ES6 引入了一种新的原始数据类型 Symbol

目前有 7 种原始类型, 1 种引用类型

原始类型:

引用类型 Object

Symbol 特性

  1. Symbol 值通过 Symbol 函数生成,不能使用 new 命令
  2. Symbol 值是唯一的,可作为对象的唯一属性
  3. Symbol 接受一个字符串作为参数,作为 Symbol 实例的描述,用于区别
  4. 如果传入的不是字符串,则会转为字符串,如果是对象,会调用 toString 方法
  5. 作为对象 key 时,不会被 for...in, for...of, Object.keys(), Object.getOwnPropertyNames(), JSON.stringify() 遍历到或返回。

类型转换

  1. 可以显示转换为字符串,但不能与其他类型进行运算
let sym = Symbol()

const str = `some str with ${sym}` // TypeError: Cannot convert a Symbol value to a string

String(sym) // 'Symbol()'
sym.toString() // 'Symbol()'
  1. 可以转换为布尔值,但不能转换为数值
let sym = Symbol()

Boolean(sym) // true
Number(sym) // TypeError
sym + 2 // TypeError

参数值

如果传给 Symbol 的参数是一样的,生成的 Symbol 并不是同一个 Symbol :

Symbol('abc') === Symbol('abc') // false

传入的参数是作为 Symbol 的一个描述。

如果要获取这个描述,有两种方法:

  1. 把 Symbol 显示转换为字符串: Symbol('abc').toString() // 'Symbol(abc)'
  2. ES2019 提供的实例属性 Symbol('abc').description // 'abc'

应用

作为对象唯一键值

使用 Symbol 作为 key 可保证不出现同名属性,导致对象属性被覆盖。

作为常量

用于定义一组常量,例如:

const logLevel = {
  DEBUG: Symbol('debug'),
  INFO: Symbol('info'),
  WARN: Symbol('warn'),
}

在上述常量中,我们其实并不关心 logLevel.DEBUG 具体是什么值,只需要保证其唯一就行,因此可以用 Symbol 来保证其中的属性值都不相等。有点类似 TypeScript 中的枚举 enum ,在编写代码时,我们不关心 enum 里面具体的值是什么,在运行时会自动编译成 1, 2, 3 这些在枚举中唯一的值。

定义非私有,但希望只用于内部的方法

Symbol 值作为对象 key ,不会被常规方法遍历到,除非用 Object.getOwnPropertySymbols 这类方法,因此可以用 Symbol 来定义一些一定程度上私有的内部方法:

let size = Symbol('size');

class Collection {
  constructor() {
    this[size] = 0;
  }

  add(item) {
    this[this[size]] = item;
    this[size]++;
  }

  static sizeOf(instance) {
    return instance[size];
  }
}

let x = new Collection();
Collection.sizeOf(x) // 0

x.add('foo');
Collection.sizeOf(x) // 1

Object.keys(x) // ['0']
Object.getOwnPropertyNames(x) // ['0']
Object.getOwnPropertySymbols(x) // [Symbol(size)]

Symbol.for() 与 Symbol.keyFor()

Symbol.for() 接受一个字符串参数,返回在全局环境中已经注册的 Symbol ,如果没有找到,则新建一个 Symbol

Symbol('abc') === Symbol('abc') // false

Symbol.for('def') === Symbol.for('def') // true

{
  let s = Symbol('sss')
  s === Symbol.for('sss') // false
}

Symbol.keyFor() 返回一个已登记的 Symbol 类型的 key ,与 Symbol.description 类似

let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"

let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined

Symbol.for() 这个方法是在全局登记的,因此可以跨 iframe 或 service worker 取到同一个 Symbol 值。

Symbol 相关的内容太多,这边不再列了。

参考

http://es6.ruanyifeng.com/#docs/symbol