一. 概述
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元无门槛券
私享最新 技术干货