Symbol
1. 前言
在 ES5 中基础数据类型有 5 种:Boolean
、Null
、Undefined
、Number
、String
,ES6 新增了一个基础数据类型 Symbol
符号、代号的意思,它是独一无二的,也就是说使用它声明的变量是独一无二的。引入这个数据类型有什么作用呢?
我们知道在 ES5 中, 对象的属性名都是字符串,容易造成属性名冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突,ES6 引入 Symbol 就可以解决这个问题。不仅如此 Symbol 的使用还有很多,在元编程中也发挥很大的作用。下面我们就来看看 Symbol 的 使用。
2. 语法详解
2.1 基本语法
Symbol([description])
参数 | 描述 |
---|---|
description | (可选)是字符串类型,对 symbol 的描述 |
2.2 基本语法
var s1 = Symbol();
var s2 = Symbol();
console.log(s1 === s2) // false
console.log(typeof s1) // symbol
2.3 带描述的 Symbol
在 Symbol
中可以传入一些参数,来描述定义的 Symbol
类型的值。
var s1 = Symbol('imooc');
var s2 = Symbol('imooc');
console.log(s1) // Symbol(imooc)
console.log(s1 === s2) // false
let s = Symbol({name: 'imooc'});
console.log(s); // Symbol([object Object])
上面的代码中,Symbol
接收的参数是一个对 Symbol
的描述,即使两个 Symbol
接收相同值,两个值也是不一样的。另外,如果传入的描述符是对象类型,内部会将描述的内容进行 toString
操作,所以返回的结果是 [object Object]
。
3. 作为对象的 key
对象上的 key,可以用取值表达式 (中括号) 的方式取出来,作为对象上的属性,如下:
var s = Symbol('imooc');
var obj = {
[s]:
}
obj // {Symbol(imooc): 1}
obj[s] // 1
上面的代码,使用 Symbol
声明了一个变量,然后作为对象的 key 给它赋值。取值的时候只能使用中括号的方式,因为这里的 s
是变量不能使用点的方式。下面是对 obj 对象用 for...in
进行的遍历。
for(let key in obj) {
console.log(obj[key])
}
// undefined
上面的代码对 obj 对象进行迭代,但是没有打印出对应的值,说明用 Symbol
来声明的属性是不可枚举的。如果想要获取到这个属性可以使用 Object.getownPropertySymbols(obj)
获取。使用 Object.keys()
、Object.getownPropertyNames()
、JSON.stringify()
也是不能返回想要的结果。
Object.getownPropertySymbols(obj); // [Symbol(imooc)]
Object.keys(obj); // []
Object.getownPropertyNames(obj); // []
JSON.stringify(obj); // "{}"
var s = Symbol('imooc');
s.description; // "imooc"
4. Symbol.for () 和 Symbol.keyFor ()
Symbol.for(key)
方法也是声明变量使用,不同的是 Symbol.for(key)
是在全局作用域下声明的。它会根据给定的键 key
从运行时的 symbol
注册表中找到对应的 symbol
。如果找到了,则返回它。否则,新建一个与该键关联的 symbol
,并放入全局的 symbol
注册表中,如果有已经声明了的 symbol
则不回重复声明。
let s1 = Symbol.for('imooc');
let s2 = Symbol.for('imooc');
function fn() {
return Symbol.for('imooc');
}
console.log(s1, s2) // Symbol(imooc) Symbol(imooc)
console.log(s1 === s2) // true
console.log(fn() === s1) // true
上面的代码中可以看出来,使用 Symbol.for(key)
无论在哪里进行声明,都不会影响它们的值。
Symbol.keyFor()
通过 key
值获取 symbol
的描述:
let s1 = Symbol.for('imooc');
console.log(Symbol.keyFor(s1)) // imooc
5 实战案例
var person = {
Tom: {sex: '男', age: },
David: {sex: '男', age: },
David: {sex: '女', age: },
}
console.log(person); // {Tom: {sex: "男", age: 18}, David: {sex: "女", age: 16}}
var person = {
Tom: {sex: '男', age: },
[Symbol('David')]: {sex: '男', age: },
[Symbol('David')]: {sex: '女', age: },
}
console.log(person)
// {Tom: {sex: "男", age: 18}, Symbol(David): {sex: "男", age: 17}, Symbol(David): {sex: "女", age: 16}}
这样就可以解决属性名冲突的问题,需要注意的是使用这样的方式定义对象数据存在一个问题,就是使用 for...in
或者使用 Object.keys()
遍历时 Symbol 属性的数据不会被遍历到,上文有具体说明。所以,如果想要遍历到对象的值可以通过 Reflect.ownKeys()
去获取对象的 key,然后进行循环操作。
for (let key of Reflect.ownKeys(person)) {
console.log(person[key])
}
// {sex: "男", age: 18}
// {sex: "男", age: 17}
// {sex: "女", age: 16}
5.2 消除魔术字符串
function getArea(shape, options) {
let area = ;
switch (shape) {
case 'Circle':
area = * Math.pow(options.radius, )
break;
case 'Square':
area = options.width * options.height;
break;
}
return area;
}
getArea('Circle', { radius: }); // 314
getArea('Square', { width: , height: }); // 100
const shapeType = {
circle: 'Circle',
triangle: 'Square'
}
function getArea(shape, options) {
let area = ;
switch (shape) {
case shapeType.circle:
area = * Math.pow(options.radius, )
break;
case shapeType.square:
area = options.width * options.height;
break;
}
return area;
}
getArea(shapeType.circle, { radius: }); // 314
getArea(shapeType.square, { width: , height: }); // 100
const shapeType = {
circle: Symbol('Circle'),
triangle: Symbol('Square')
}