为啥需要 Symbol
ES5
里面对象的属性名都是字符串,如果你需要使用人提供的对象,你对这个对象有哪些属性也不是很清楚,但又想为这个对象新增一些属性,那么你新增的属性名就很可能和原来的属性名发送冲突,显然我们是不希望这种情况发生的。所以,我们需要确保每个属性名都是独一无二的,这样就可以防止属性名的冲突了。因此,ES6
里就引入了Symbol
,用它来产生一个独一无二的值。
Symbol 是什么
Symbol
实际上是ES6
引入的一种原始数据类型,除了Symbol
,JavaScript
还有其他 5 种数据类型,分别是Undefined, Null. Boolean, String, Number
, 对象 ,这 5 种数据类型都是ES5
中就有的。
怎么生成一个 Symbol 类型的值
既然我们已经知道了
Symbol
是一种原始的数据类型,那么怎么生成这种数据类型的值呢?Symbol
值是通过Symbol
函数生成的,如下:
let sym = Symbol();
console.log(sym); // Symbol()
console.log(typeof sym); // symbol
Symbol 函数不能用 new
Symbol
函数不是一个构造函数,前面不能用new
操作符。所以Symbol
类型的值也不是一个对象,不能添加任何属性,它只是一个类似于字符型的数据类型。如果强行在Symbol
函数前加上new
操作符,会报错,如下:
let sym = new Symbol();
console.log(sym); // Uncaught TypeError: Symbol is not a constructor
Symbol 函数的参数
字符串作为参数
用上面的方法生成的
Symbol
值不好进行区分,Symbol
函数还可以接受一个字符串参数,来对产生的Symbol
值进行描述,方便我们区分不同的Symbol
值。
let s1 = Symbol('s1');
let s2 = Symbol('s2');
let s3 = Symbol('s3');
console.log(s1); // Symbol('s1')
console.log(s2); // Symbol('s2')
console.log(s1 === s2); // false
console.log(s2 === s3); // false
1.给
Symbol
函数加了参数之后,控制台输出的时候可以区分到底是哪一个值;
Symbol
函数的参数只是对当前Symbol
值的描述,因此相同参数的Symbol
函数返回值是不相等的;
对象作为参数
如果
Symbol
函数的参数是一个对象,就会调用该对象的toString
方法,将其转化为一个字符串,然后才生成一个Symbol
值。所以,说到底,Symbol
函数的参数只能是字符申。
Symbol值不可以进行运算
既然
Symbol
是一种数据类型,那我们一定想知道Symbol
值是否能进行运算。告诉你,Symbol
值是不能进行运算的,不仅不能和Symbol
值进行运算,也不能和其他类型的值进行运算,否则会报错。Symbol
值可以显式转化为 字符串 和 布尔值, 但是不能转为 数值。
var mysym1 = Symbol('my symbol');
mysym1.toString(); // Symbol('my symbol')
String(mysym1) // Symbol('my symbol')
var mysym2 = Symbol();
Boolean(mysym2); // true
Number(mysym2); // 报错
Symbol 作属性名
symbol
就是为对象的属性名而生,那么Symbol
值怎么作为对象的属性名呢?有下面几种写法:
let a = {};
let s4 = Symbol();
// 第一种写法
a[s4] = 'mySymbol';
// 第二种写法
a = {
[s4]: 'mySymbol'
}
// 第三种写法
Object.defineProperty(a, s4, { value: 'mySymbol' });
a.s4; // undefined
a.s4 = 'mySymbol';
a[s4] // undefined
a['s4']// 'mySymbol'
1,使用对象的
Symbol
值作为属性名时,获取相应的属性值不能用点运算符;
2.如果用点运算符来给对象的属性赋
Symbol
类型的值,实际上属性名会变成一个字符串,而不是一个Symbol
值;
3,在对象内部,使用
Symbol
值定义属性时,Symbol
值必须放在方括号之中,否则只是一个字符串。
Symbol值作为属性名的遍历
使用
for.in
和for..of
都无法逸历到Symbol
值的属性,Symbol
值作为对象的属性名,也无法通过Object.keys()
、Object.getOwnPropertyNames()
来获取了。
但是,不用担心,这种平常的需求肯定是会有解决办法的。
我们可以使用
Object.getownPropertySymbols()
方法获取一个对象上的Symbol
属性名。也可以使用Reflect.ownKeys()
返回所有类型的属性名,包括常规属性名和Symbol
属性名
let s5 = Symbol('s5');
let s6 = Symbol('s6');
let a = {
[s5]: 's5',
[s6]: 's6'
}
Object.getOwnPropertySymbols(a); // [Symbol('s5'), Symbol('s6')]
a.hello = 'hello';
Reflect.ownKeys(a); // [hello', Symbol('s5'), Symbol('s6')]
利用
Symbol
值作为对象属性的名称时,不会被常规方法遍历到这一特性,可以为对象定义一些非私有的但是又希望只有内部可用的方法。
只能拿到
String
类型的属性名
Object.getOwnPropertyNames(testObject)
Object.keys(testObject)
只能拿到
Symbol
类型的属性名
Object.getOwnpropertySymbols(testObject)
能拿到对象 任何类型 的属性名
Reflect.ownKeys(testObject)
Symbol.for() 和 Symbol.keyFor()
Symbol.for
(函数也可以用来生成Symbol
值,但该函数有一个特殊的用处,就是可以重复使用一个Symbol
。
let s1 = Symbol('s1');
let s2 = Symbol('s2');
let s3 = Symbol.for('s1'); // Symbol(s1)
let s4 = Symbol.for('s2'); // Symbol(s2)
let s11 = Symbol.keyFor(s1); // undefined
let s33 = Symbol.keyFor(s3); // s1
console.log(s11); // undefined
console.log(s33); // s1
Symbol.for()
函数要接受一个字符串作为参数,先搜索有没有以该参数作为名称的Symbol
值,如果有,就直接返回这个Symbol
值,否则就新建并返回一个以该字符串为名称的Symbol
值。
Symbol.keyFor()
函数是用来查找一个Symbol
值的登记信息的,
Symbol()
写法没有登记机制,所以返回undefined
;
而
Symbol.for()
函数会将生成的Symbol
值登记在全局环境中,所以Symbol.keyFor()
函数可以查找到用Symbol.for()
函数生成的Symbol
值。
内置Symbol值
ES6提供了11个内置的
Symbol
值,分别是
Symbol.hasInstance,
Symbol.isConcatSpreadable、
Symbol.species,
Symbol.match,
Symbol.replfice.
Symbol.search,
Symbol.split,
Symbol.iterator
Symbol.toPrimitive,
Symbol.toStringTag,
Symbol.unscopables
等。