ES6-Symbols

#背景
ES5中,属性都可以通过点方法来获取和赋值。这也带来了一个问题,当我想给一个类增加属性的时候,除非我看完这个类的所有细节,否则我很难知道一个属性名是否已经被占用。而ES6为了解决这个问题,引入了一个新的原始类型Symbol,它表示独一无二的值,来避免重复的命名。

加入Symbol之后,JS中的基础类型共有:

  • undefined
  • Null
  • Boolean
  • String
  • Number
  • Object
  • Symbol

#使用

初始化

Symbol的初始化方法很简单,但是要注意,它不是对象(Object),所以不能用new初始化

1
let s = Symbol();

Symbol没有属性,是一个类似于string的数据类型,我的理解,Symbol其实就是独一无二的string。

1
s.name = 'symbol'; //error

Symbol可以通过加参数的形式来区分不同的Symbol,如下:

1
let s = Symbol('foo');

注意,即使使用相同的参数来初始化,得到的实例也是不同的,比如:

1
2
3
4
5
6
let a = Symbol('foo');
let b = Symbol('foo');
let same = a === b;
console.log(same); //false

如果想要获取以某个string为参数的Symbol,在之后会讲到Symbol.for()函数。

赋值

使用Symbol给属性赋值,用法和string属性赋值基本相似,但是不可以用.,要用[]。以下是三种赋值方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let s = Symbol();
let a = {};
// method 1
a[s] = 'hello';
//method 2
a = {
[s] = 'hello';
}
//method 3
Object.defineProperty(a, s, {value: 'hello'});

使用场景

Symbol的一个场景是上述提到的,给一个类增加属性,而避免属性重复。

第二是使用在常量中,switch case来保证唯一性。

1
2
3
4
5
6
7
8
9
10
11
let CASE1 = Symbol();
let CASE2 = Symbol();
switch (case) {
case CASE1 :{
return 1;
}
case CASE2 :{
return 2;
}
}

注意事项(和string属性不同的地方)

最主要的区别,使用 for ... infor ... of 或者Object.keys()Object.getOwnPropertyNames()不会返回Symbol

如果想获取对象的所有Symbols,使用函数getOwnPropertySymbols

有个新的API,Reflect.ownKyes,会返回所有的String属性和Symbol属性

Symbol.keyFor和Symbol.for

Symbol.for

Symbol会返回一个新的实例,无论参数是什么,上面已经将到了。

1
2
3
4
let a = Symbol('foo');
let b = Symbol('foo');
let same = a === b; //false

而Symbol.for则会先搜索是否有以这个参数初始化的Symbol,如果有则放回这个Symbol,否则初始化一个新的,实现逻辑如下

1
2
3
4
5
6
7
Symbol.for(params) = {
if (searchSymbolInitWithParams){
return found_symbol;
} else {
return Symbol(params);
}
}

和第一个例子对比看下:

1
2
3
4
let a = Symbol.for('foo');
let b = Symbol.for('foo');
let same = a === b; //true

如果Symbol和Symbol.for混用呢?

1
2
3
4
let a = Symbol('foo');
let b = Symbol.for('foo');
console.log(a===b); //false

这说明两者是没有打通的,即Symbol初始化的实例无法被Symbol.for找到

Symbol.keyFor

用Symbol.for来初始化的Symbol,如何知道它的初始化参数呢,这里提供了一个方法: Symbol.keyFor

1
2
let a = Symbol.for('foo');
let b = Symbol.Keyfor(a);// 'foo'

注意,Symbol初始化不能用这个函数

1
2
let a = Symbol('foo');
let b = Symbol.Keyfor(a);// undefined

注意:Symbol.for 为Symbol值登记的是全局的,这意味着在不同的iframe都可以取到同一个值。