今天学习Symbol
一、Symbol
1.概述
???在ES5中对象的属性名都是字符串,这就很容易出现命名冲突。当你引用了别人定义的对象,但你又想对这个对象添加新的方法,那么新的方法和旧的方法可能就会产生冲突。 ???这种情况下,如果有一种机制保证每个对象的属性名都是独一无二的就好了,这样就可以从根本上阻止命名冲突,这也就是ES6引入Symbol的原因。 ???ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。Symbol是JavaScript中的第七种数据类型,这七种分别是数值(Number)、字符串(String)、布尔值(Boolean)、空(Null)、未定义(undefined)、对象(Object)、Symbol。
2.Symbol的创建
symbol可以通过Symbol函数来创建,但是Symbol函数前不能使用new命令。我们要了解到Symbol是一种新的原始数据类型,而使用new命令创建的是一个对象,所以Symbol函数前不能使用new命令。
let s=Symbol('this is a Symbol');
console.log(typeof s);
可以看到,typeof运算符的结果表明变量s是Symbol数据类型,而不是其他的数据类型。 Symbol不是对象,所以也无法添加属性,Symbol是一种类似字符串的数据类型。 Symbol可以接收一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者在转换为字符串时比较容易区分。
let s1=Symbol('firstSymbol');
let s2=Symbol('secondSymbol');
console.log(s1);
console.log(s2);
console.log(s1.toString());
console.log(s2.toString());
如果Symbol的参数是一个对象时,就会调用该对象的toString方法,将其转为字符串,然后才生成一个Symbol值。
const obj={
a:'123'
}
console.log(obj.toString());
const sym=Symbol(obj);
console.log(sym);
const obj2={
toString(){
return '我是obj的toString()方法';
}
}
const sym2=Symbol(obj2);
console.log(sym2);
要注意,前面提到了Symbol代表一个独一无二的值,而Symbol函数的参数只是表示对当前Symbol值的描述对值不产生影响,因此相同参数的Symbol函数的返回值也是不相等的。
let s1=Symbol();
let s2=Symbol();
console.log(s1===s2);
let q1=Symbol('man');
let q2=Symbol('women');
console.log(q1===q2);
let e1=Symbol('same');
let e2=Symbol('same');
console.log(e1===e2);
要注意,Symbol类型的值不能与其他类型的值进行运算,否则会报错。
let s1=Symbol('im a symbol');
let str='im a str';
s1+str;
Symbol可以显示得转换为一些其他的数据类型,比如字符串(String)和布尔类型(Boolean),但是不能转为数值类型(Number)
let s1=Symbol('im a symbol');
console.log(String(s1));
console.log(s1.toString());
console.log(Boolean(s1));
console.log(!s1);
console.log(Number(s1));
console.log(s1+1);
3.Symbol.prototype.description
创建Symbol的时候,可以添加一个描述。 但是在读取这个描述时,需要将Symbol显示转为字符串。
let s1=Symbol('im a symbol');
console.log(String(s1));
console.log(s1.toString());
这么做很不方便,所以ES2019给Symbol的原型添加了一个属性description,直接返回Symbol的描述。
let s1=Symbol('im a symbol');
console.log(s1.description);
4.作为属性名的Symbol
由于每个Symbol值都是独一无二的,这意味着使用Symbol类型的值作为对象的属性名就能保证不会出现同名的属性。这对于一个对象由多个模块组成的情况非常有用,能够防止某一个键被不小心改写或覆盖。
let mySymbol=Symbol();
let a={};
a[mySymbol]='Hello!';
let b={
[mySymbol]:'Hello!'
};
let c={};
Object.defineProperty(c,mySymbol,{value:'Hello!'});
console.log(a[mySymbol]);
console.log(b[mySymbol]);
console.log(c[mySymbol]);
注意Symbol值作为对象属性名时,不能用点运算符。
a.mySymbol = 'Hello!';
console.log(a[mySymbol]);
console.log(a['mySymbol'] );
因为点运算符后面总是字符串,所以不会读取mySymbol作为标识名所代指的那个值,导致a的属性名实际上是一个字符串,而不是一个Symbol值。
let s1=Symbol();
let obj={
[s1]:function(arg){console.log(arg);}
};
obj[s1](123);
在对象的内部,使用Symbol值定义属性时,Symbol值必须放在方括号之中。 如果Symbol值不放在方括号中,该属性的键名就是字符串s1,而不是s1所代表的那个Symbol值。 要注意一点,Symbol值作为属性名时,该属性时还是公开属性,不是私有属性。
|