[toc]
### 普通符号
符号的基本概念:
- 符号是ES6中新增的一种数据类型。
- 它通过使用Symbol(符号描述)来创建。符号描述为定义符号时针对该符号的描述信息,仅用于方便识别创建该符号的用途。
- 符号设计的初衷,是为了给对象设置私有属性
- 对象的私有属性是指该属性只能在对象内部使用,外部是无法调用的这些属性。
符号的特点:
- 符号没有字面量。
- 使用typeof得到的类型为symbol。
- **每次调用Symbol得到的符号永远不相等,无论符号名是否相同**
- 符号可以做为对象的属性名存在,这种属性称之为符号属性。
```js
const syb1 = Symbol('定义一个符号'); //创建了一个符号类型的变量syb1
const obj = {
a: 1,
b: 2,
[syb1]: 'abc'
//把syb1做为obj的一个符号属性,定义时需用[]包裹符号变量名,属性的内容可以随便定义。
//函数内部调用符号属性也需要用[符号变量名]来调用。
}
```
- 开发者可以通过一些设计,让这些属性无法通过常规方式被外界访问。
>通过创建立即执行函数,在函数内创建符号属性。可以在常规的情况下实现私有化属性。
```js
//例如:游戏角色每次攻击伤害值的取值过程
const hero = (function () {
const getRandom = Symbol();
return {
attack: 30,
hp: 300,
defence: 10,
gongji() { //攻击
//伤害:攻击力*随机数(0.8~1.1)
const dmg = this.attack * this[getRandom](0.8, 1.1);
console.log(dmg);
},
[getRandom](min, max) { //根据最小值和最大值产生一个随机数
return Math.random() * (max - min) + min;
}
}
})()
//调用该方法会产生一个getRandom符号属性,因为是立即执行函数。函数执行完毕即销毁了,所以在外部无法调用getRandom属性。
//定义一个类的时候也可以使用同样的方法,返回一个类表达式。
```
- 符号属性是不能被枚举的,因此在for-in循环中无法读取到符号属性,Object.key方法也无法读取到符号属性。
- Object.getOwnPropertyNames 尽管可以得到所有无法枚举的属性,但是仍然无法读取到符号属性
- ES6 新增 Object.getOwnPropertySymbols 方法,可以读取符号
- 符号无法被隐式转换,因此不能被用于数学运算、字符串拼接或其他隐式转换的场景,但符号可以显式的转换为字符串,通过 String 构造函数进行转换即可,console.log 之所以可以输出符号,是它在内部进行了显式转换
>该章节重点撑握符号类型的基本特性,会使用符号属性创建一个私有属性。
### 共享符号
当我们在某个对象或类中创建了一个符号类型的私有属性,但又想在其它文件的代码中调用该符号属性。ES6中提供了共享符号的方法。
```js
//Symbol.for("符号名/符号描述"),用来创建一个共享符号
const sy1 = Symbol.for('abc')
const sy2 = Symbol.for('abc')
sy1 === sy2 //返回的结果为true,说明虽然是两个变量,但其指向同一个符号,可以在不同的位置使用。
```
共享符号可以从外部直接调用
```js
const obj = {
a:1,
b:2,
[Symbol.for('cc')]: 'ccc'
}
console.log(obj[Symbol.for('cc')]); //返回的结果为ccc,即可以用这种方法在外部调用共享符号属性。
```
### 知名(公共、具名)符号
- 知名符号是一些具有特殊含义的共享符号,通过Symbol的静态属性得到。
- ES6延续了ES5的思想:减少魔法(指不清楚实现原理的一些方法),暴露内部实现。因此,ES6使用知名符号暴露了某些场景的内部实现。
1. Symbol.hasInstance
该符号用于定义构造函数的静态成员,它将影响 instanceof 的判定
```js
obj instanceof A
//该符号等效于
A[Symbol.hasInstance](obj) // Function.prototype[Symbol.hasInstance]
```
2. [扩展] Symbol.isConcatSpreadable
该知名符号会影响数组的 concat 方法,只有true和false两个参数。
当设置为true时,concat方法会将拼接的数组分割成每一个独立的值,追加到对应的数组中。
当设置为false时,concat方法会将拼接的数组直接追加到对应的数组中。
```js
let arr = [0, 1]
let arr1 = [2, 3]
arr1[Symbol.isConcatSpreadable] = true; //拼接进行分割
let newarr = arr.concat(arr1); //newarr的值为[0,1,2,3]
arr1[Symbol.isConcatSpreadable] = false; //拼接不进行分割
let newarr = arr.concat(arr1); //newarr的值为[0,1,[2,3]]
```
3. [扩展] Symbol.toPrimitive
该知名符号会影响类型转换的结果
```js
const obj = {
a: 1,
b: 2
}
//正常情况下obj在进行基本运算时会启动隐式类型转换,如obj+123,结果为:[object object]123,obj最终会被使用toString方法转为字符串。
obj[Symbol.toPrimitive] = function(type){
return 2
}
//当使用obj的Symbol.toPrimitive知名符号,可以定义一个函数。该函数的返回值就是隐式类型转换后的值。
const res = obj+123; //例如使用了上面的知名符号后,obj隐式类型转换返回的结果变成2了,所以res的结果为125
//该知名符号定义的函数会返回一个type参数,该参数用来表示当前是在会哪种方式进行转换,返回值有default、number、String。
```
4. [扩展] Symbol.toStringTag
该知名符号会影响 Object.prototype.toString 的返回值。
如果把该符号写入一个类或对象中,指定一个名称,那么该名称会替换toString方法返回的第二位的内容
例如:一个对象toString后返回[object Object],说明这是一个对象。若在该对象中加入[Symbol.toStringTag] = 'Student'。那么再次执行toString后会返回[object Student]。