学习内容
- Symbol是新的原型数据类型
- 声明方式
- 应用场景
单词释义
Symbol:(独一无二的)
n. 象征,代表; 标志; 符号,记号
JS七个原始数据类型
undefine、null、number、string、boolean、object、symbol
Symbol是新的原型数据类型
第一种声明方式:
let s1 = Symbol()
let s2 = Symbol()
let s3 = Symbol('three') // 传入字符串用于对symbol的描述
let s4 = Symbol('four')
console.log(s1)
console.log(s2)
console.log(s1 === s2)
console.log(s3)
console.log(s4)
console.log(s3 === s4)
-------------------------
Symbol()
Symbol()
false
Symbol(three)
Symbol(four)
false
第二种声明方式
当参数是对象的时候,会自动调用该对象的toString方法,转化成一个字符串,再生成一个symbol的值:
const obj = {
name : 'Sure'
}
console.log(Symbol(obj))
const obj1 = {
name : 'Sure',
toString() { // 对象内部function的简写形式
return this.name
}
}
console.log(Symbol(obj1))
------------------------------------------
Symbol([object Object])
Symbol(Sure) // 自动调用了obj1中的toString()
不能像声明对象的方法去声明Symbol(),比如:
let s = Symbol()
s.name = 'aaa'
console.log(s)
-------------------------------------------
Symbol() // 什么都没有,因为symbol不是一个对象
Symbol可以理解为不能重复的字符串,就是把它理解成字符串就行。
下面看一个symbol的API:
let s = Symbol()
let t = Symbol('desc')
console.log(s.description)
console.log(t.description)
------------------------
undefined
desc
第三种声明方式
let s1 = Symbol.for('orion')
let s2 = Symbol.for('orion')
console.log(s1 === s2)
-----------
true
上面代码中s1和s2相等,是因为这样的声明方式是全局的,当再次现出Symbol.for('orion')时,会去全局中查找是否有Symbol.for('orion'),如果有,就把值赋给s2。
再看把symbol.for写在方法内部,发现也是在全局中存在的:
function test() {
return Symbol.for('test')
}
const x = test()
const y = Symbol.for('test')
console.log(x === y)
------------
true
与Symbol.for对应的还有一个Symbol.keyFor,作用是返回一个已经全局登记的Symbol类型的key:
let s1 = Symbol('test')
console.log(Symbol.keyFor(s1))
let s2 = Symbol.for('test')
console.log(Symbol.keyFor(s2))
----------------------------------------
undefined // Symbol('test')不属于全局定义
test // 使用全局定义
Symbol应用场景一:
班级中有重名的同学
下面这种写法,第二个李四把第一个李四覆盖掉了:
const grade = {
李四: {addr : 'aaa', tel : 'bbb'},
李四: {addr : 'ccc', tel : 'ddd'}
}
console.log(grade)
--------------------------------
李四: {addr: 'ccc', tel: 'ddd'}
下面这种写法,把key用变量表示,还是会有覆盖的情况:
const stu1 = '李四'
const stu2 = '李四'
const grade = {
[stu1] : {addr : 'aaa', tel : 'bbb'},
[stu2]: {addr : 'ccc', tel : 'ddd'}
}
console.log(grade)
-------------------------------
李四: {addr: 'ccc', tel: 'ddd'}
用Symbol作为key,保证key不冲突
这时Symbol的作用就体现出来了,两个李四全都可以输出:
const stu1 = Symbol('李四')
const stu2 = Symbol('李四')
const grade = {
[stu1] : {addr : 'aaa', tel : 'bbb'},
[stu2]: {addr : 'ccc', tel : 'ddd'}
}
console.log(grade)
console.log(grade[stu1]) // grade[stu1] 等价于 grade.key
console.log(grade[stu1])
---------------------------------------
Symbol(李四): {addr: 'aaa', tel: 'bbb'}
Symbol(李四): {addr: 'ccc', tel: 'ddd'}
{addr: 'aaa', tel: 'bbb'}
{addr: 'aaa', tel: 'bbb'}
Symbol应用场景二:
一定程度上可以保护类中的某些属性,比如使用for in无法遍历出对象中的symbol属性
const sym = Symbol('syu6')
class User {
constructor(name) {
this.name = name
this[sym] = 'syu.com'
}
getName() {
return this.name + this[sym]
}
}
const user = new User('Sure')
// console.log(user.getName())
// 使用for in遍历对象的属性
for(let key in user) { // 无法取得symbol属性
console.log(key)
}
for(let key of Object.keys(user)) { // 无法取得symbol属性
console.log(key)
}
for(let key of Object.getOwnPropertySymbols(user)){ // 只能取到symbol属性
console.log(key)
}
console.log('-------------')
for(let key of Reflect.ownKeys(user)) { // 普通属性和symbol属性都可以取到
console.log(key)
}
----------------------------------
name
name
Symbol(syu6)
-------------
name
Symbol(syu6)
Symbol应用场景三:
消除魔术字符串
魔术字符串是指多次出现,被多次引用,每次都手写的话容易出错:
function getArea(shape) {
let area = 0
switch(shape) {
case 'Triangle' :
arae = 1
break
case 'Circle' :
area = 2
break
}
return area
}
console.log(getArea('Triangle')) // 这里的'Triangle'就是魔术字符串,写两次很麻烦,还容易出错
看下面的实现方式就好很多:
const shapeType = {
// triangle : 'Triangle',
// circle : 'Circle'
// 把上面两行改为下面这样,其实我们并不关心symbol()是什么,主要是key就可以说明我们想的东西了
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)) // 这里的'Triangle'就是魔术字符串,写两次很麻烦,还容易出错
---------
1