一种新的原始数据类型
定义
它的意思是符号、独一无二的意思,可以理解为不能重复的字符串
原始数据类型
- number -string,
- bool
- null,
- undefined,
- object,
- Symbol
声明方式
不加任何描述信息的Symbol
// Symbol 表示的是独一无二的象征
let s1 = Symbol()
let s2 = Symbol()
console.log(s1); // Symbol()
console.log(s2); // Symbol()
console.log(s1 === s2); // false
这种方式由于没有任何的描述,因此并不知道s1、s2是什么
将对象作为参数来作为Symbol的描述信息
把一个字符串作为参数传进去,用于对Symbol的描述
let s1 = Symbol('foo')
let s2 = Symbol('bar')
console.log(s1); // Symbol(foo)
console.log(s2); // Symbol(bar)
console.log(s1 === s2); // false
将对象作为参数来作为Symbol的描述信息
参数是对象的时候会自动调用该对象的toString方法,将它转成字符串,然后把这字符串作为Symbol的描述
let obj = {
name: 'lee'
}
let s = Symbol(obj)
console.log(s); // Symbol([object Object])
let obj = {
name: 'lee',
toString(){
return this.name
}
}
let s = Symbol(obj)
console.log(s); // Symbol(lee)
Symbol 不是对象
不能拿对象的方式去对待symbol,它更多的是一种特殊的不能重复的字符串
// Symbl不是对象
let s = Symbol()
s.name = 'lee'
console.log(s); // Symbol()
// Symbol前也不能使用new
let s = new Symbol() // Symbol is not a constructor
symbol.dscription 输出symbol的描述信息
通过Symbol.for()来声明
let s1 = Symbol.for('foo')
console.log(s1); // Symbol(foo)
发现结果和普通的Symbol声明方式一样,那么去区别是什么呢?
let s1 = Symbol.for('foo')
let s2 = Symbol.for('foo')
console.log(s1 === s2); // true
let s1 = Symbol('foo')
let s2 = Symbol('foo')
console.log(s1 === s2); // false
为什么通过Symbol.for的方式创建的symbol值就相等,而普通的方式创建的Symbol值就不相等呢?
===> 通过Symbol.for创建的symbl的值实际上是定义在全局的环境中,当第二次声明Symbol.for('foo')的时候就会去全局中找,前面是否声明过描述叫foo的,那么下一个就指向上一个,因此s1和s2是同一个
===> 通过Symbol('foo')的方式创建symbol的时候就不是在全局环境下创建的,而是每次都是申明一个新的描述信息叫foo的symbol
如果使用Symbol.for('foo')创建100个描述信息叫foo的symbol实际上就只声明了一次
如果使用Symbol('foo')创建100个描述信息叫foo的symbol实际上就声明了100次
通过Symbol.for('foo')创建的symbol就已经是全局环境,并不管当前的Symbol.for('foo')是否在全局环境下声明的
// 即使Symbol.for定义在函数中,实际上它也是全局的
function foo() {
return Symbol.for('foo')
}
const x = foo()
const y = Symbol.for('foo')
console.log(x === y); // true
Symbol.keyFor()
返回一个已经在全局登记的Symbl类型的key / 或者说描述信息
- 通过
Symbol.for('foo')声明的symbol可以找到key - 但是通过
Symbol('foo')普通的方式声明的symbol就找不到key(因为它不是声明在全局环境下)
const s1 = Symbol('foo')
console.log(Symbol.keyFor(s1)); // undefined
const s2 = Symbol.for('foo')
console.log(Symbol.keyFor(s2)); // foo
应用场景
1. 把Symbol作为对象的key,以保证当前的key并不产生冲突
相同的key,后定义的会覆盖先定义的
例如: 班级中存在重名的2个同学,这2个人只是名字一样,但是它表示2个不同的人
// 3个人,只输出2个人,后定义的会覆盖先定义的人
const grade = {
张三: {address:'xxx', tel: '183xxx'},
李四: {address:'yyy', tel: '151xxx'},
李四: {address:'zzz', tel: '135xxx'}
}
console.log(grade); // {张三: {address:'xxx', tel: '183xxx'},李四: {address:'zzz', tel: '135xxx'}}
这样不满足需求!!!
将对象的key改成变量呢?
对象的key也可以是一个变量
// 还是被认为是相同的key,还是会出现覆盖
const stu1 = '李四'
const stu2 = '李四'
const grade = {
[stu1]: { address: 'yyy', tel: '151xxx' },
[stu2]: { address: 'zzz', tel: '135xxx' }
}
console.log(grade); // { address: 'zzz', tel: '135xxx' }
这样也不行
使用Symbol来做
const stu1 = Symbol('李四')
const stu2 = Symbol('李四')
const grade = {
[stu1]: { address: 'yyy', tel: '151xxx' },
[stu2]: { address: 'zzz', tel: '135xxx' }
}
console.log(grade); // { address: 'zzz', tel: '135xxx' }
这样,对象的key的类型由只能是字符串变为也可以是Symbol类型,凡是Symbol定义的属性都表示独一无二的,这样就可以保证和其他的名字产生冲突
2. 在一定程度上将类class中的某些属性保护起来(当然也有办法读到它)
思路:父类中定义Symbol作为key,用一般的遍历的方式遍历对象的实例是取不到key的
const sym = Symbol('symbol')
class User {
constructor(name) {
this.name = name
this[sym] = 'symbol'
}
getName() {
return this.name + this[sym]
}
}
const user = new User('lee')
console.log(user.getName()); // lee symbol
for(let key in user){
console.log(key); // name
}
for(let key of Object.keys(user)){
console.log(key); // name
}
for(let key of Object.getOwnPropertySymbols(user)){
console.log(key); // Symbol(symbol)
}
for(let key of Reflect.ownKeys(user)){
console.log(key); // name Symbol(symbol)
}
3. 用Symbol消除魔术字符串(不关系对象key对应的value)
代码中多次出现相同的字符串(让代码形成强耦合),很多地方都要引用,手敲的话可能还会敲错
// 如下面的字符串‘Triangle’就被在多个地方使用
function getArea(shape){
let area = 0
switch(shape) {
case 'Triangle':
area = 1
break
case 'Circel':
area = 2
break
}
return area
}
console.log(getArea('Triangle'));
那么有什么办法能只写一遍呢?
const shapeType = {
triangle: 'Triangle',
circle: 'Circle'
}
function getArea(shape){
let area = 0
switch(shape) {
case shapeType.triangle:
area = 1
break
case shapeType.circle:
area = 2
break
}
return area
}
console.log(getArea(shapeType.triangle));
上面的方法在一定程度上消除了“魔术字符串”
对于shapeType对象来说,我们好像并不关心属性triangle后面对应的值到底是什么样的值,我们只要让属性名triangle和属性名circle不产生冲突就行
const shapeType = {
triangle: Symbol(),
circle: Symbol()
}
function getArea(shape){
let area = 0
switch(shape) {
case shapeType.triangle:
area = 1
break
case shapeType.circle:
area = 2
break
}
return area
}
console.log(getArea(shapeType.triangle)); // 1