一. 概述
1. 基本含义
由于对象的属性名都是字符串,所以很容易出现属性名的冲突
在使用了某个别人的对象后,想给对象添加新的属性mixin模式,新属性的名字就可能和现有的属性发生冲突
Symbol数据类型的引入就是为了从根本上防止属性名冲突。
ES6中引入的Symbol数据类型表示独一无二的值,属于基本数据类型的一种
//?1.?使用Symbol函数生成
var?a=Symbol("foo")
console.log(a);//Symbol(foo)
console.log(typeof?a);//symbol
//?2.?使用new?Symbol会报错(因为没有包装对象,没有构造器函数)
var?b=new?Symbol("b");//TypeError:?Symbol?is?not?a?constructor
// 3. Symbol值即使参数一致,也不是同一个变量!
console.log(a===Symbol("foo"));//false
console.log(a==Symbol("foo"));//false
Symbol函数前不能使用new命令,否则会报错,这是因为Symbol值不是对象,只是类似字符串的数据类型,但是没有包装对象
Symbol值即使参数一致,也不是同一个变量!因为参数仅仅起到描述作用
2. 参数
如果Symbol可以有一个参数,表示该值的描述信息,也可以不添加该参数
如果参数是一个对象,那么就会调用该对象的toString()方法,将其转换为字符串,然后才生成Symbol值
//?1.没有参数
var?a=Symbol()
//?console.log(a);/Symbol()
//?2.?参数为字符串
var?b=Symbol("b")
//?console.log(b);//Symbol(b)
//?3.?参数为其他基本数据类型(先转换为字符串再转换为Symbol值,注意undefined会转为空)
var?c=Symbol(1);//Symbol(1)
//?var?c=Symbol(false);//Symbol(false)
//?var?c=Symbol(undefined);//Symbol()
//?var?c=Symbol(null);//Symbol(null)
var?c=Symbol(NaN);//Symbol(NaN)
console.log(c)
//?4.?参数为引用对象类型(即使无属性,也是Symbol([object?Object]))
var?d=Symbol({a:1})
console.log(d);//Symbol([object?Object])
var?e=Symbol({e:3,c:'ss'})
console.log(e);//Symbol([object?Object])
var?p=Symbol({})
console.log(p);//Symbol([object?Object])
//?5.定义对象的toString方法
var?obj={
toString(){
return?'我是自定义对象的方法'
}
}
console.log(Symbol(obj));//Symbol(我是自定义对象的方法)
3. 转换
Symbol值不能和其他值进行运算,会报错!
Symbol值可以转换为字符串,通过toString()或者String()
Symbol值也可以转为布尔值,但是不可以转为数值
//?1.Symbol运算
//?console.log(Symbol(1)+Symbol(2));//Cannot?convert?a?Symbol?value?to?a?number
//?console.log(Symbol(1)+1);//Cannot?convert?a?Symbol?value?to?a?number
//?console.log("i?am"+Symbol("ww"));//Cannot?convert?a?Symbol?value?to?a?string
//?2.Symbol值可以显式转为字符串
console.log(String(Symbol("2")));//Symbol(2)
console.log(Symbol("ss").toString());//Symbol(ss)
//?3.Symbol值可以转为布尔值
console.log(Boolean(Symbol(1)));//true
//?4.Symbol值不能转为数值
console.log(Number(Symbol(1)));//Cannot?convert?a?Symbol?value?to?a?number
二. Symbol描述
虽然可以通过String(),toString()来获取Symbol值的描述,但是会带有Symbol()前缀
ES2019提供了description属性来直接返回描述console.log(Symbol("foo").description);//foo
三. Symbol作为属性名
Symbol常常用于作为属性名,防止属性被覆盖!
1.有以下三种用法
var?a=Symbol("foo")
//?1.
//?var?obj={}
//?obj[a]="ww";
//?console.log(obj[a]);//ww
//2
/*??var?obj={
[a]:"ww"
}
console.log(obj[a]);//ww?*/
//3.Object.defineProperty(),不设置enumerable的话,定义的属性默认是不可枚举的
var?obj={}
Object.defineProperty(obj,a,{value:"ww"})
console.log(obj[a]);//ww
//{value:?"ww",?writable:?false,?enumerable:?false,?configurable:?false}
console.log(Object.getOwnPropertyDescriptor(obj,a));
2.注意事项
如果属性a属于Symbol类型,那么不能使用obj.a 形式!因为会被识别为字符串
var?a=Symbol()
var?obj={}
//?1.?使用点运算符,后面的变量被视为字符串
obj.a="ss"
console.log(obj.a);//ss
//?2.?使用中括号形式
obj[a]="hello"
console.log(obj[a]);//hello,此时指向的是Symbol类型的变量
console.log(obj.a);//ss,此时指向的是a这个属性!
作为对象方法使用
//?1.相当于属性
var?a=Symbol()
var?obj={
[a]:function(){?return?1}
}
console.log(obj[a]());//1
//2.对象方法
let?b={
[a](){
return?2
}
}
console.log(b[a]());//2
四. 消除魔术字符串
魔术字符串就是在代码中多次出现,和代码形成强耦合的字符串,应该尽量消除魔术字符串,改用变量代替
使用Symbol消除的一个例子
//?1.?存在魔术字符串时
function?getData(str,options){
var?res;
switch(str){
case?"str":
res=?options.a+options.b;
}
return?res;
}
console.log(getData("str",{a:3,b:2}));//?str这个字符串形成了强耦合
//?2.?使用Symbol解决
var?a={
e:Symbol()
}
function?tryData(str,options){
var?res;
switch(str){
case?a.e:
res=?options.a+options.b;
}
return?res;
}
console.log(tryData(a.e,{a:3,b:2}))
五.Symbol属性名的遍历
for...in;for...of循环;Object.keys(),Object.values(),Object.entries();Object.getOwnPropertyNames();JSON.stringfy()都不会出现Symbol属性名
Object.getOwnPropertySymbols()方法可以获取到对象的所有Symbol属性名,但是不能获取到除了Symbol之外的属性
另外Reflect.ownKeys()可以获取到所有类型的键名,包括Symbol,不可枚举
var?obj={a:1,1:'e',3:'y',b:99}
obj[Symbol('q')]='symbol'
obj[Symbol('p')]='p'
//1.for?in,for...of
for(var?item?in?obj){
console.log(item);//1,3,a,b
}
//?obj?is?not?iterable?报错
//?for(var?item?of?obj){
//??console.log(item)
//?}
//2.JSON.stringfy
console.log(JSON.stringify(obj));//{"1":"e","3":"y","a":1,"b":99}
//3.?Object.getOwnPropertySymbols
console.log(Object.getOwnPropertySymbols(obj));//[Symbol(q),?Symbol(p)]
//4.?Reflect.ownkeys()
console.log(Reflect.ownKeys(obj));//["1",?"3",?"a",?"b",?Symbol(q),?Symbol(p)]
六.Symbol.for()和Symbol.keyFor()
有时我们需要知道是否存在该名称的Symbol变量,或者进行修改,但是直接使用Symbol()会新增一个变量
1. Symbol.for()
使用Symbol.for()可以接受一个字符串作为参数,然后搜索是否存在使用过Symbol.for()的同名变量
//?1.?Symbol,还有一个Symbol.for
var?a=Symbol('a')
var?aa=Symbol.for('a')
console.log(a===aa);//false
//2.?两个都是用了Symbol.for()
var?b=Symbol.for('b')
var?bb=Symbol.for('b')
console.log(b===bb);//true
//3.?空参数
var?c=Symbol.for()
var?cc=Symbol.for()
console.log(c===cc);//true
当不存在同名Symbol.for()定义的方法时,定义之后会注册到全局中
而使用Symbol()方法不会把变量注册到全局中!
2. Symbol.keyFor()
Symbol.keyFor()可以返回已在全局注册(Symbol.for())的Symbol类型的参数名
并且Symbol.for()即使在函数中,也是把变量注册到全局的
//?1.?Symbol.keyFor()
var?s1=Symbol.for('a')
var?s2=Symbol('b')
console.log(Symbol.keyFor(s1));//a
console.log(Symbol.keyFor(s2));//undefined,没有在全局注册
//?2.在函数中使用Symbol.for()也是注册到全局中
function?func(){
return?Symbol.for('bar')
}
var?x=func();//?注册bar到全局了
var?y=Symbol.for('bar')
console.log(x===y);//true
七. Symbol的一些方法
1. Symbol.hasInstance()
foo instanceof MyClass相当于 MyClass[Symbol.hasInstance](foo)
class?MyArray{
static?[Symbol.hasInstance](instance){
return?Array.isArray(instance)
}
}
console.log([1]?instanceof?MyArray);//true,static静态属性才能被类直接使用,去掉则false
2. Symbol.match()
对象的Symbol.match属性指向一个函数,当执行str.match(obj)的时候该属性存在则调用,返回方法的返回值
str.match(func),相当于 [symbol.macth](str)
//?使用方法一:使用类的实例
class?MyClass{
[Symbol.match](str){
return?'字符串'
}
}
console.log("hello?blog".match(new?MyClass()));//字符串
//?方法二;使用对象属性
var?obj={
[Symbol.match](str){
return?str;
}
}
console.log('hhha'.match(obj));//hhha
3. Symbol.iterator()
只要调用...拓展运算符,那么相当于执行了对象里面存在[Symbol.iterator]方法
var?obj={
*?[Symbol.iterator]()?{
yield?1;
yield?2;
yield?3;
}
}
console.log([...obj]);//[1,?2,?3]
领取专属 10元无门槛券
私享最新 技术干货