1、概述
ES5的对象属性名都是字符串,这容易造成属性名的冲突。如果有一种机制,保证每一个属性的名字都是独一无二的就好了,这就是ES6引入symbol的原因。ES6引入了一种新的数据类型Symbol,表示独一无二。他是javascript语法的第七种数据结构,前六种:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)
let s=Symbol();
typeof s;// 'symbol'
注意⚠️:Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始值,不是对象,不能添加属性.Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述
let s1=Symbol('foo');
let s2=Symbol('bar');
s1 //Symbol(foo)
s2 //Symbol(bar)
s1.toString() //"Symbol(foo)"
s2.toString() //"Symbol(bar)"
Symbol函数的参数只是表示对当前Symbol值的描述,因此相同的参数的Symbol函数的返回值是不相等的。
//没有参数的情况
let s1=Symbol();
let s2=Symbol();
s1===s2 //false
//有参数的情况
let s1=Symbol('foo');
let s2=Symbol('foo');
s1===s2 //false
Symbol值不能与其他类型的值进行运算,会报错
let sym=Symbol('My symbol');
"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)'
另外,Symbol值也可以转为布尔值,但是不能转为数值
let sym=Symbol();
Boolean(sym); //true
!sym //false
if(sym) {...}
Number(sym) // TypeError
sym + 2 // TypeError
2、Symbol.prototype.description
ES2019提供了一个实例属性description,直接返回Symbol的描述,用于对象的属性名,就能保证不会出现同名的属性。
const sym=Symbol('foo');
sym.description //foo
3、作为属性名的Symbol
由于每一个Symbol值都是不相等的,这意味着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"
因为点运算符后面总是字符串,所以不会读取mySymbol作为标识名所指代的那个值,导致a的属性名实际上是一个字符串,而不是一个Symbol值。同理,在对象的内部,使用Symbol值定义的属性时,Symbol值必须放在方括号之中。
let s=Symbol();
let obj={
[s]: function (arg) {...}
};
obj[s](123);
//采用增强的对象写法,上面代码的obj对象可以写的更简洁一些。
let obj={
[s](arg) {...}
}
Symbol类型还可以用于定义一组常量,保证这组常量的值都是不相等的。
const log={};
log.levels={
DEBUG: Symbol('debug'),
INFO: Symbol('info'),
WARN: Symbol('wran')
}
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');
}
}
4、实例:消除魔术字符串
魔术字符串指的是,在代码之中多次出现,与代码形成强耦合的某一个具体的字符串或者数值。风格良好的代码,应该尽量消除魔术字符串,改进含义清晰的变量代替。
5、属性名的遍历
Symbol作为属性名,遍历对象的时候,该属性不会出现在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnpropertyName()、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.getOwnPropertySymbol(obj);
objectSymbols //[Symbol(a), Symbol(b)]
另一个新的 API,Reflect.ownKeys()方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。
let obj={
[Symbol('my_key')]: 1,
enum: 2,
nonEnum: 3
}
Reflect.ownKeys(obj);
// ["enum", "nonEnum", Symbol(my_key)]
6、Symbol.for(),Symbol.keyFor()
let s1=Symbol.for('foo');
let s2=Symbol.for('foo');
s1===s2; //true
Symbol.keyFor()方法返回一个已登记的Symbol类型的key。
let s1=Symbol.for("foo");
Symbol.keyFor(s1) //"foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined s2为未登记的Symbol值,所以返回undefined
7、实例:模块的Singleton模式
8、内置的Symbol值
Symbol.hasInstance
Symbol.inConcatSpreadable
Symbol.species
Symbol.match
Symbol.replace
Symbol.search
Symbol.split
Symbol.iterator