Symbol类型详解
-
symbol类型通过Symbol函数生成,用于表示独一无二的值(不使用new关键字,直接调用即可创建。例:
const s = Symbol("str")
) -
即使使用相同的字符串来创建Symbol类型的值,这些值也是不同的
-
symbol值可以调用 toString方法,也可以通过Boolean()转为 布尔值
a.symbol本身转为布尔值时为true,取反为false
b.toString调用后会返回Symbol(str) -
在ES6中,支持用表达式 (变量)作为属性名,但表达式必须放在方括号里,由于每个Symbol值都是独一无二的,就可以用Symbol值作为属性名,且不会和其他属性名重复
-
使用symbol值作为属性名的属性访问只能使用name和 这个symbol值,例:
obj[name]
-
属性名遍历
a. symbol类型的属性并不是私有属性,但不能通过for in遍历到,也不能被
Object.keys()
,Object。getOwnProperNames()
,Json.stringify()
获取到
b. symbol类型属性名可以通过Object.getOwnPropertySymbol()
获取,但只能获取到symbol类型的属性名
c. 使用ES6新提供的Reflect对象的 静态方法Reflect.ownKeys()
可以获取全部属性名 ,包括symbol类型和非symbol类型 -
symbol的静态方法
a.
Symbol.for()
使用
Symbol.fro()
方法传入字符串,会先检查有没有使用这个字符串调用Symbol.for
创建的symbol值,如果有,就返回该值,没有则进行创建通过Symbol.for创建的symbol值是全局范围内的
b.
Symbol.keyFor()
调用Symbol.keyfor传入一个symbol值可以返回该值在全局注册的键名,例
const sym = Symbol.for("aaa"); console.log(Symbol.keyFor(sym)); //会返回aaa
-
ES6的11个内置Symbol值
a. Symbol.hasInstance
对象的Symbol.hasInstance指向一个内部方法,如果给一个对象设置了以Symbol.hasInstance为属性名的方法,当其他对象使用instanceof来判断是否为该对象实例时,会调用这个方法,传入的参数为被判断的这个对象
const obj = { [Symbol.hasInstance](obj2){ console.log('obj2'); //调用后会打印出{a:111}这个对象 } }; console.log({a:111} istanceof obj); //{a:111}会作为obj2传入Symbol.hasInstance
b. Symbol.isConcatSpreadable
该属性是一个可读写的布尔值,默认值为undefined,该属性控制数组是否能被扁平化,当值为false时,数组不能被扁平化,为true或undefined则可以,例:
let arr1 = [1,2]; console.log([].concat(arr1,[3,4])); //打印结果为[1,2,3,4] console.log(arr1[Symbol.isConcatSpreadable]); //此时该属性值为默认的undefined arr1[Symbol.isConcatSpreadable] = true; //把属性值设置为true console.log([].concat(arr1,[3,4]));//结果同上 console.log(arr1[Symbol.isConcatSpreadable]); //属性值为true arr1[Symbol.isConcatSpreadable] = false; //再设置为false console.log([].concat(arr1,[3,4])); //此时的结果变成了[Array(2), 3, 4] //展开后是[[1, 2, Symbol(Symbol.isConcatSpreadable): false],3,4] //这里的Symbol(Symbol.isConcatSpreadable): false]不是一个元素,而是一个属性 console.log(arr1[Symbol.isConcatSpreadable]); //false
c. Symbol.species
定义一个类C,使其继承自Array,再给类C创建一个实例对象c,c就能继承Array原型对象上的方法,通过c的map方法衍生一个a对象,分别打印
a instanceof C
和a instanceof Array
,发现结果都为true,说明a既是C的实例对象,也是Array的实例对象class C extends Array{ getName(){ return 'aaa'; } }; const c = new C(1,2,3); const a = c.map(item=>item+1) console.log(a instanceof Array) console.log(a instanceof C)//两个结果都为true console.log(a.getName())//aaa
如果需要让a只是Array的实例而不是C的实例,就需要使用Symbol.species,给C定义一个名为Symbol.specied的静态get存取器方法,并在该方法中返回要构造衍生数组的构造函数
class C extends Array{ static get[Symbol.species](){ retrun Array; } getName(){ return 'aaa'; } }; const c = new C(1,2,3); const a = c.map(item=>item+1) console.log(a instanceof Array)//打印true console.log(a instanceof C)//结果为false console.log(a.getName())//a不是C的实例,也不能调用getName方法,会报错a.getName is not a function
d. Symbol.match,Symbol.replace,Symbol.search,Symbol.split
这四个都指向一个内部方法,当在字符串上调用match,replace,search,和split方法时,会调用这个方法
let obj = { [Symbol.match](str){ return str.length } }; console.log('aaaaa'.match(obj));//返回值为5
e. Symbol.iterator
数组的Symbol.iterator方法指向该数组的默认遍历器方法,该属性是可写的,可以自定义遍历器方法
f. Symbol.toPrimitive
Symbol.toPrimitive指向一个内部方法,当对象被转为原始类型是会调用这个方法,这个方法的参数是该对象被转为的类型
const obj = { [Symbol.toPrimitive](type){ console.log(type); } }; const a = obj++ //调用方法后会打印number
g. Symbol.toStringTag
对象的Symbol.toStringTag属性可以是一个字符串也可以是一个存取器get方法,当对象调用toString方法时,会返回
[object 返回值]
let obj = { //属性值为存取器get方法 get [Symbol.toStringTag](){ return 'aaa'; } }; let obj2 = { //字符串属性值 [Symbol.toStringTag]:'bbb' } console.log(obj.toString()); //打印[object aaa] console.log(obj2.toString()); //打印[object bbb]
h. Symbol.unscopables
对象的该属性指向一个对象,可以控制这个对象的属性是否被with环境过滤,
const obj={ a:111, b:222, c:333, } obj[Symbol.unscopables]={ a:true, b:false } with(obj){ // console.log(a); //因为对a设置为了true,所以a属性会被with环境过滤掉,不能通过with访问,会报错 console.log(b);//222 console.log(c);//333 } console.log(obj[Symbol.unscopables]); //{a: true, b: false}