ES6学习笔记15:Symbol

Symbol基础

Symbol是ES6中新增的一种原始数据类型(其他原始数据类型分别是:Undefined,Null,String,Number,Boolean,Object),表示唯一值。在ES6中,一个对象的属性名既可以是字符串,也可以是Symbol类型;当我们用Symbol当做一个独一无二的标识时,可以简单地理解为它是一个字符串,当然,Symbol不是一般的对象,不能为其添加属性。

1
2
3
let foo = Symbol();
let bar = Symbol('symbol');
console.log(typeof foo); // symbol

注意定义一个Symbol类型的变量时不需要使用new关键字;通过Symbol返回的类型是'symbol'

Symbol函数可以接受一个字符串作为参数,用于描述一个Symbol,但即使两个Symbol的描述是相同的,也不表示他们相等。

1
2
3
4
5
6
let foo = Symbol();
let bar = Symbol();
console.log(foo === bar); // false
let foo = Symbol('symbol');
let bar = Symbol('symbol');
console.log(foo === bar); // false

此外Symbol函数也可以接受一个对象作为参数,不过该对象会被toString()方法转化为字符串。Symbol类型的变量不能直接参与计算,也不能显式转换为数值,但可以显式转换为字符串或者布尔值。

作为属性名

1
2
3
4
var sym = Symbol('foo');
var obj = { [sym]: 1 };
obj[sym]; // 1
obj[Object(sym)]; // 还是 1

作为属性名的用法一般是用扩展对象字面量(即中括号的形式)定义。

替代Magic String

Magic String是代码中和程序耦合度较高的字符串,一般来说为了便于维护,我们会在一个程序或者一个系统中将Magic String统一写在一个地方,将其作为常量储存起来,但是大多数时候这些常量的内容并没有太多实际意义,我们需要的只是一个唯一标识符,这个时候我们就可以使用Symbol代替Magic String。

来看如下代码(示例来自参考3)。

1
2
3
4
5
6
7
8
9
10
11
function getArea(shape, options) {
let area = 0;
switch (shape) {
case 'Triangle': // 魔术字符串
area = .5 * options.width * options.height;
break;
/* ... more code ... */
}
return area;
}
getArea('Triangle', { width: 100, height: 100 }); // 魔术字符串

我们看到'Triangle'这个字符串这样直接放在代码中难以维护,这里虽然只用到了两次,但在规模更大的程序或者工程中可能出现更多次,而当这样的字符串大量存在时,维护这样的代码将是灾难性的。因此我们可以考虑将其抽取出来,放在一个专门保存常量的地方。

但同时,存储的这个常量的具体值对程序并没有实际意义,这里我们就可以考虑采用Symbol。代码如下所示。

1
2
// const.js
export const triangle = Symbol();
1
2
3
4
5
6
7
8
9
10
11
12
// main.js
import * as shapeType from './const';
function getArea(shape, options) {
var area = 0;
switch (shape) {
case shapeType.triangle:
area = .5 * options.width * options.height;
break;
}
return area;
}
getArea(shapeType.triangle, { width: 100, height: 100 });

全局Symbol

在上面代替常量的小节中,我们仍然使用模块的导入导出方式来使用Symbol。实际上还有一种方式,只要保证Symbol在某处(一般是系统启动的时候被执行的某个文件,类似于上面的const.js)执行了,它就会作为全局常量保存起来,然后我们可以通过Symbol.for方法来获取该常量。

1
2
3
4
5
6
7
8
Symbol.for('foo'); // 创建一个全局Symbol
Symbol.for('foo'); // 获取一个已经创建了的全局Symbol
// 使用for采用相同的参数创建的Symbol会返回相同的Symbol引用
Symbol.for('bar') === Symbol.for('bar'); // true
Symbol('bar') === Symbol('bar'); // false
// 参数依然是一个描述字符串
var sym = Symbol.for('mario');
sym.toString(); // "Symbol(mario)"

为了避免命名冲突,建议给描述字符串的加一个前缀。

1
2
Symbol.for('io.ouyang.foo');
Symbol.for('io.ouyang.bar');

其他

此外Symbol还能用于辅助实现JavaScript中OOP中的私有属性,本文不再详述。读者可以阅读参考和后面有关JavaScript中OOP的专门的笔记。

知识点总结

  1. Symbol是一种表示的是独一无二的值新的原始数据类型;
  2. Symbol的定义方法和基本用法;
  3. Symbol用于表示属性名,替代Magic String。

参考

  1. BabelJS - Learn ES2015
  2. Proxy - MDN
  3. ECMAScript 6 入门 Symbol - 阮一峰
  4. Private Properties - MDN