JS笔记《数据类型—Symbol》

81 阅读3分钟

概述

  • ES5的对象属性名都是字符串,容易造成属性名的冲突。Symbol可以保证属性名的独一无二。是一种原始数据类型。ES6中,对象的属性名可以有两种类型,一种字符串,另一种就是新增的Symbol
let s = Symbol();
typeof s // "symbol"
  • Symbol()函数不能使用new命令,否则会报错。由于Symbol值不是一个对象,所以也不能添加属性。算是一个类似于字符串的数据类型。
  • Symbol()函数接收一个字符串作为参数,表示对Symbol实例的描述。两个相同参数的Symbol值也是不相等的。
let s1 = Symbol('s1')
let s2 = Symbol('s2')
s1 // Symbol(s1)
s2 // Symbol(s2)

let s1 = Symbol();
let s2 = Symbol();
s1 === s2 // false 

let s1 = Symbol('foo');
let s2 = Symbol('foo');
s1 === s2 // false
  • 如果Symbol的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个Symbol值。
let a = {b: 1};
Symbol(a); // Symbol([object Object])
  • Symbol不能与其它类型的值进行运算,会报错。但是可以转为字符串和布尔,但不能转为数值。
let sym = Symbol('symbol');
"your symbol is " + sym  // TypeError: Cannot convert a Symbol value to a string

String(sym)  // 'Symbol(symbol)'
Boolean(sym) // true

Number(sym)  // TypeError: Cannot convert a Symbol value to a number
sym + 2      // TypeError: Cannot convert a Symbol value to a number

作为属性名

  • 由于每一个Symbol值都是不相等的,意味着只要Symbol值作为标识符用于对象的属性名,就能保证不会出现同名的属性。作为属性名时,不能用点运算符
let mySymbol = Symbol()

// 写法一
let obj = {}
obj[mySymbol] = 'Hello'  // 使用[]定义,否则会被当作字符串属性名

// 写法二
let obj = {
  [mySymbol]: 'Hello'   // // 使用[]定义,否则会被当作字符串属性名
}

// 写法三
let obj = {};
Object.defineProperty(obj, mySymbol, {value: 'Hello!'});


obj    // {Symbol(): 'Hello'}
obj[mySymbol]  // 'Hello'
obj.mySyobol   // undefined 不能用.运算符

属性名的遍历

  • Symbol不是一个私有属性,但不会被for in、for of、Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。
  • 可以使用专属方法Object.getOwnPropertySymbols()获取指定对象的所有Symbol属性名。该方法返回一个数组。
let a = Symbol('a');
let b = Symbol('b');
let obj = {
    [a]: 'hello',
    [b]: 'world',
    c: '!'
}
Object.keys(obj)  // ['c']
Object.getOwnPropertyNames(obj)  // ['c']

Object.getOwnPropertySymbols(obj)  // [Symbol(a), Symbol(b)]

作为常量

  • 如果对于常量的值没有要求,只要确保不会跟其他属性值冲突,这时就可以使用Symbol
const COLORS = {
  black: Symbol(),
  yellow: Symbol(),
  red: Symbol()
}

function getLevel(color) {
  switch (color) {
    case (COLORS.black):
      return 'the log level -- info'
      break;
    case (COLORS.yellow):
      return 'the log level -- warning'
      break;
    case (COLORS.red):
      return 'the log level -- error'
      break;
    default:
      return 'level is error'
  }
}

getLevel(COLORS.black);  // 'the log level -- info'
getLevel(COLORS.yellow); // 'the log level -- warning'

实例属性

description

  • 返回Symbol值的描述。
let sym = Symbol('symbol');
sym.description   // 'symbol'

静态方法

for()

  • 接受一个字符串作为参数,全局搜索有没有以该参数作为名称的Symbol值。如果有就返回这个Symbol值,否则就新建一个以该字符串为描述的Symbol值,并将其登记到全局(无论是不是在函数内创建的)。
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');

s1 === s2 // true
  • Symbol.for()Symbol()都会生成新的Symbol。区别是前者会先在全局环境搜索有没有已存在的,如果未创建就创建一个,并全局登记。而后者不会,后者每调用一次都是生成新的Symbol
Symbol.for("bar") === Symbol.for("bar")  // true

Symbol("bar") === Symbol("bar")  // false

keyFor()

  • 返回一个已登记Symbol值的key
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"


let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined  未登记的Symbol,所以返回 undefined