ES6高级特性--Symbol

Posted Nicolas Shawn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6高级特性--Symbol相关的知识,希望对你有一定的参考价值。

Symbol

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 javascript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)

# 简单使用
let s = Symbol();
typeof s
// "symbol"

# 接受字符串作为参数
let s1 = Symbol(\'foo\');
let s2 = Symbol(\'bar\');
s1 // Symbol(foo)
s2 // Symbol(bar)
s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"
String(s1)

# 可以使用toString()方法
const obj = {
  toString() {
    return \'abc\';
  }
};
const sym = Symbol(obj);
sym // Symbol(abc)

# 相同的参数值是不相等的
// 没有参数的情况
let s1 = Symbol();
let s2 = Symbol();
s1 === s2 // false
// 有参数的情况
let s1 = Symbol(\'foo\');
let s2 = Symbol(\'foo\');
s1 === s2 // false

# 无法参与运算
let sym = Symbol(\'My symbol\');
"your symbol is " + sym
// TypeError: can\'t convert symbol to string
`your symbol is ${sym}`
// TypeError: can\'t convert symbol to string

# Symbol 值可以显式转为字符串、布尔值、但是不能转为数字
let sym = Symbol(\'My symbol\');
String(sym) // \'Symbol(My symbol)\'
sym.toString() // \'Symbol(My symbol)\'

let sym = Symbol();
Boolean(sym) // true
!sym  // false
if (sym) {
  // ...
}
Number(sym) // TypeError
sym + 2 // TypeError

# 作为属性名的symbol
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = \'Hello!\';
// 第二种写法
let a = {
  [mySymbol]: \'Hello!\'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: \'Hello!\' });
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
# 注意,Symbol 值作为对象属性名时,不能用点运算符。
const mySymbol = Symbol();
const a = {};
a.mySymbol = \'Hello!\';
a[mySymbol] // undefined
a[\'mySymbol\'] // "Hello!"

let s = Symbol();
let obj = {
  [s]: function (arg) { ... }
};

obj[s](123);
// ==> 使用对象增强法更简洁的写法:
let obj = {
  [s](arg) { ... }
};

Symbol应用场景

# 场景一:常量定义
const log = {};
log.levels = {
  DEBUG: Symbol(\'debug\'),
  INFO: Symbol(\'info\'),
  WARN: Symbol(\'warn\')
};
console.log(log.levels.DEBUG, \'debug message\');
console.log(log.levels.INFO, \'info message\');

// 定义常量 场景2
const COLOR_RED    = Symbol();
const COLOR_GREEN  = Symbol();
function getComplement(color) {
  switch (color) {
    case COLOR_RED:
      return COLOR_GREEN;
    case COLOR_GREEN:
      return COLOR_RED;
    default:
      throw new Error(\'Undefined color\');
    }
}


# 场景二:消除魔术字符串
const shapeType = {
  triangle: \'Triangle\'
};

function getArea(shape, options) {
  let area = 0;
  switch (shape) {
    case shapeType.triangle:
      area = .5 * options.width * options.height;
      break;
  }
  return area;
}
getArea(shapeType.triangle, { width: 100, height: 100 });
// 使用symbol 替换如下:
const shapeType = {
  triangle: Symbol()
};

# 场景3:

Symbol的特殊注意事项

# 属性名的遍历
/**
Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。

但是,它也不是私有属性,有一个Object.getOwnPropertySymbols()方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
**/
const obj = {};
let a = Symbol(\'a\');
let b = Symbol(\'b\');
obj[a] = \'Hello\';
obj[b] = \'World\';
const objectSymbols = Object.getOwnPropertySymbols(obj);
objectSymbols
// [Symbol(a), Symbol(b)]

// 用例2
const obj = {};
const foo = Symbol(\'foo\');
obj[foo] = \'bar\';
for (let i in obj) {
  console.log(i); // 无输出
}
Object.getOwnPropertyNames(obj) // []
Object.getOwnPropertySymbols(obj) // [Symbol(foo)]

// 新的 API,Reflect.ownKeys()方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。
let obj = {
  [Symbol(\'my_key\')]: 1,
  enum: 2,
  nonEnum: 3
};
Reflect.ownKeys(obj)
//  ["enum", "nonEnum", Symbol(my_key)]
// 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)]
// 以上这段代码,对象x的size属性是一个 Symbol 值,所以Object.keys(x)、Object.getOwnPropertyNames(x)都无法获取它。这就造成了一种非私有的内部方法的效果。

Symbol.for(),Symbol.keyFor()

# 有时,我们希望重新使用同一个Symbol值 Symbol.for()
let s1 = Symbol.for(\'foo\');
let s2 = Symbol.for(\'foo\');
s1 === s2 // true
// 问题:Symbol.for()与Symbol()有啥区别?
// 前者会被登记在全局环境中供搜索,后者不会。
Symbol.for("bar") === Symbol.for("bar")
Symbol("bar") === Symbol("bar")

# Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key。
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined

// I*Symbol.for()为 Symbol 值登记的名字,是全局环境的,不管有没有在全局环境运行。
function foo() {
  return Symbol.for(\'bar\');
}
const x = foo();
const y = Symbol.for(\'bar\');
console.log(x === y); // true

以上是关于ES6高级特性--Symbol的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript高级ES6常见新特性:词法环境letconst模板字符串函数增强SymbolSetMap

ES6新特性使用小结

ES6 前端必学ES6,看完掌握新特性

ES6新特性总结解构赋值模板字符串Symbol

es6 新增数据类型Symbol

ES6 symbol公开符号