由来
ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。
基础数据类型
ES6中引入了一种新的数据类型symbol
,表示独一无二的值。它是JS的第七种基础数据类型,前六种分别为:undefined、null、Boolean、String、Number、Object
由于Symbol是一种基础数据类型,所以当我们使用typeof去检查它的类型的时候,它会返回一个属于自己的类型symbol
let s = Symbol()
typeof s // "symbol"
我们可以通过调用Symbol()函数来创建一个Symbol实例,但是不能用 new 命令,因为 Symbol 是原始数据类型,不是对象。
可选参数
Symbol()可以接受一个字符串作为参数,为新创建的 Symbol 提供描述,用来显示在控制台或者作为字符串的时候使用,便于区分。
let sym = Symbol('zlear')
let sym2 = Symbol()
sym // Symbol(zlear)
sym2 // Symbol()
ES2019 提供了一个实例属性description,直接返回 Symbol 的描述
let sym = Symbol('fedezer');
sym.description // "fedezer"
注意,Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。
// 没有参数的情况
let s1 = Symbol();
let s2 = Symbol();
s1 === s2 // false
// 有参数的情况
let s1 = Symbol('foo');
let s2 = Symbol('foo');
s1 === s2 // false
唯一性
对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
var a={}, b=Symbol('123'), c=Symbol('123');
a[b]='b';
a[c]='c';
a[b] // 'b'
a[c] // 'c'
a // {Symbol(123): "b", Symbol(123): "c"}
顺便拓展了解一下键名:
- 对象的键名只能是字符串和 Symbol 类型。
- 其他类型的键名会被转换成字符串类型。
- 对象转字符串默认会调用 toString 方法。
属性名
Symbol 作为属性名,但该属性不会出现在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。
let obj = {
[Symbol('name')]: 'Zlear',
age: 18,
sex: 'woman'
}
Object.keys(obj) // ['age', 'sex']
for (let p in obj) {
console.log(p) // 分别会输出:'age' 和 'sex'
}
Object.getOwnPropertyNames(obj) // ['age', 'sex']
JSON.stringify(obj) // {"age":18,"title":"Engineer"}
Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名
Object.getOwnPropertySymbols(obj) // [Symbol(name)]
阮一峰 ECMAScript 6 入门