1、前言
Symbol出生的原因
es5中对象属性只能是字符串的形式,但是要求对象的属性名称必须是唯一,这就和容易出现属性名称冲突的问题。例如引入一个第三方库,你想在第三方库基础上扩展,名称很容易造成冲突。
Symbol简介
es6引入Symbol, 是一个原始类型的值(不是对象),表示独一无二的值,作为js的一种数据类型。(数据类型:Number、Boolean、String、Undefined、Null、Object、Symbol)
typeof Symbol('foo') //'symbol'
注意事项
- 1、不要使用 new 调用 Symbol
- 2、相同参数 Symbol不会相等
- 3、不能与其他值进行计算
- 4、不能转成数值,但是可以显示的转换字符串或布尔值
var sym = new Symbol() //报错
var sym1 = Symbol();
var sym2 = Symbol();
sym1 === sym2 //false
sym1 + 'hello' //报错
Number(sym1) //报错
sym1.toString() //"Symbol(foo)"
String(sym1) //"Symbol(foo)"
Boolean(sym1) //true
!sym1 //false
2、Symbol的描述
Symbol.prototype.description可以获取描述,为打印控制台可以区分是哪个Symbol
//不传
var sym = Symbol();
sym.description //undefined
//字符串
var sym = Symbol('foo');
sym.description //'foo'
//对象
var sym = Symbol({a:'a'});
sym.description //[object Object]
//数组
var sym = Symbol([1,2]);
sym.description //'1,2'
//函数
var sym = Symbol(function foo(){})
sym2.description //"function foo(){}"
从上面可以看出,描述的值,相当于对对参数进行ToString()操作得到结果
3、Symbol.for()与Symbol.keyFor()
Symbol.for()能够做到使用同一个Symbol。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。
var sym1 = Symbol.for('foo');
var sym2 = Symbol.for('foo');
sym1 === sym2 //true
需要注意的是,Symbol.for为Symbol值登记的名字,是全局环境的,可以在不同的 iframe 或 service worker 中取到同一个值。
Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key
var sym = Symbol.for('foo');
Symbol.keyFor(sym) //'foo'
而对于Symbol()定义的值使用Symbol.keyFor()就会的到undefined,因为它是没有登记的
var sym = Symbol('foo');
Symbol.keyFor(sym) //undefined
4、应用
作为常量
const COLOR_RED = Symbol();
const COLOR_GREEN = Symbol();
function getComplement(color) {
switch (color) {
case COLOR_RED:
return 'red'
case COLOR_GREEN:
return 'green';
default:
throw new Error('Undefined color');
}
}
getComplement(COLOR_RED);
作为属性名称
var mysym = Symbol();
//第一种
var obj = {};
obj[mysym] = 'hello';
//第二种
var obj = {
[mysym]: 'hello'
};
//第三种
var obj = {};
Object.defineProperty(obj, mysym, {value: 'hello'})
console.log(obj[mysym]) // 'hello'
//定义函数这样简写
var obj = {
[mysym](arg){
//todo
}
};
注意:
- Symbol 值作为对象属性名时,不能用点运算符。因为点运算符后面总是字符串。
- Symbol 值作为属性名时,该属性还是公开属性,不是私有属性。
属性名遍历
Symbol 作为属性名,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。
有一个Object.getOwnPropertySymbols()方法,可以获取指定对象的所有 Symbol 属性名。Reflect.ownKeys方法可以返回所有类型的键名。
var a = Symbol('a');
var b = Symbol('b');
var obj = {
[a]: 'a',
[b]: 'b'
};
Object.getOwnPropertySymbols(obj);
//[Symbol(a), Symbol(b)]
Reflect.ownKeys(obj);
//[Symbol(a), Symbol(b)]
Object.getOwnPropertyNames(obj)
//[]
Object.keys(obj)
//[]
JSON.toString(obj)
//"[object JSON]"
将Symbol定义的属性造成了一种非私有的内部方法的效果
全局实例对象
// mod.js
const FOO_KEY = Symbol.for('foo');
function A() {
this.foo = 'hello';
}
if (!global[FOO_KEY]) {
global[FOO_KEY] = new A();
}
module.exports = global[FOO_KEY];
可以保证global[FOO_KEY]不会被无意间覆盖,但还是可以被改写
global[Symbol.for('foo')] = { foo: 'world' };
//other.js
const a = require('./mod.js');