前面说过,ES5中的对象属性名都是字符串,容易造成属性之间的冲突。比如我们在使用一个别人提供的对象,但是我想给这个对象添加自己想要的方法,这个方法的名字可能与现有的方法名字产生冲突,从而会覆盖原有的方法&属性值。如果有一种方式,可以保证每个属性都是独一无二的就好了,这样就能解决属性冲突的问题。这也是ES6推出类型Symbol的原因。
JavaScript在ES5有5种基本数据类型,分别是:Null,Undefined,String,Boolean,Number,ES6新增了一个基本数据类型Symbol,表示独一无二的值。
let s = Symbol();
console.log(typeof s); // "Symbol";
Symbol函数前不能使用new命令,否则会报错。因为生成Symbol是一个基本数据类型,不是对象。也可以这样说,因为不是对象,所以不能给该值添加属性。
如果Symbol的参数是一个对象,就会调用该对象toString方法,将其转为字符串,然后才生成一个Symbol值。
const obj = {
toString() {
return "1"
}
}
const sym = Symbol(obj);
console.log(sym); // Symbol(1)
上面说过Symbol类型是一个独一无二的值。那就说明Symbol类型不等于自己本身。
无论是没有参数还是加了参数,都不等于自己相同的值。
console.log(Symbol() === Symbol()); //false
Symbol值不能与其他类型的值进行运算,否则会报错。
Symbol值可以转为布尔值,但是不能转为数值。
写法
var mysymbol = Symbol();
var a = new Object();
a.[mysymbol] = '1';
var mysymbol = Symbol();
var a = {
[mysymbol]: '2'
}
var mysymbol = Symbol();
var a = {};
Object.defineProperty(a, mysymbol, {
value: '3'
})
不能使用点运算符,因为点运算符后面总是字符串,所以不会读取mysymbol作为标识名所指代的值,导致a的属性名实际上是一个字符串,而不是一个symbol值。
消除魔法字符串
魔术字符串指的是,在代码之中多次出现,与代码形成强耦合的某一个具体的字符串或数值。风格良好的代码,应该尽量消除魔术字符串,而由含义清晰的变量代替。
function getArea(shape, options){
var area = 0;
switch (shape) {
case 'Triangle': //魔术字符串
area = .5 * options.width * options.height;
break;
}
return area;
}
console.log(getArea('Triangle', {width: 100, height: 100})) ;
常用的消除魔术字符串的方法,就是把它写成一个变量。
var shapeType = {
shape: 'Triangle'
}
function getArea(shape, options){
var area = 0;
switch (shape) {
case shapeType.shape ://魔术字符串
area = .5 * options.width * options.height;
break;
}
return area;
}
console.log(getArea(shapeType.shape, {width: 100, height: 100})) ;
如果仔细分析,可以发现,可以发现shapeType.shape等于那个值并不重要,只要确保不会和其他shapeType的属性冲突就好了。
var shapeType = {
shape: Symbol()
}
function getArea(shape, options){
var area = 0;
switch (shape) {
case shapeType.shape ://魔术字符串
area = .5 * options.width * options.height;
break;
}
return area;
}
console.log(getArea(shapeType.shape, {width: 100, height: 100})) ;
属性名的遍历
Symbol 作为属性名,该属性名不会出现在for...in,for...of 循环中。但它也不是私有属性,有一个Objeck.getOwnPropertySymbols 方法可以获取指定对象的所有Symbol属性名。
Objeck.getOwnPropertySymbol返回一个数组,成员是当前对象的所有用作属性名的Symbol值。
var obj = {}, a = Symbol('a'), b = Symbol('b');
obj[a] = 'hellow';
obj[b] = 'word';
obj['c'] = '111';
var arr = Object.getOwnPropertySymbols(obj);
console.log(arr); // [Symbol(a), Symbol(b)]
将Objeck.getOwnPropertySymbols() 方法与for...in 循环,Objeck.getOwnPropertyNames方法进行了对比。
var obj = {}, foo = Symbol('foo');
Object.defineProperty(obj, foo , {
value: 'foobar'
});
for (let i in obj) {
console.log(i); //没有输出
}
console.log(Object.getOwnPropertySymbols(obj)); //[Symbol(foo)]
console.log(Object.getOwnPropertyNames(obj)); //[]
还有一个api——Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和Symbol。
var obj = {
[Symbol('name')]: '肖',
age: 21
}
var num = Reflect.ownKeys(obj);
console.log(num); // ["age", Symbol(name)]
重新使用同一个Symbol值,Symbol.for方法可以做到这一点。它接收一个字符串为参数,然后搜索有没有以改参数命名的Symbol值,如果有,就返回该值,没有就新建并返回一个以该字符串为名称的Symbol值。
var s = Symbol.for('foo');
var s2 = Symbol.for('foo');
console.log(s === s2); //true
Symbol.keyFor方法返回一个已登记的Symbol类型值的key。
var s = Symbol.for('foo');
Symbol.keyFor(s); // "foo"
Symbol值登记的名字是全局环境的,可以在不同的iframe 或 service worker中取到同一个值。
网友评论