├── Symbol
│ ├── 概述
│ │ └─ ES6 引入了一种新的原始数据类型`Symbol`,表示独一无二的值。它属于 JavaScript 语言的数据类型之一,其他数据类型是:`undefined`、`null`、布尔值(Boolean)、字符串(String)、数值(Number)、大整数(BigInt)、对象(Object)。
│ │ └─ `Symbol` 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
│ │ └─ `Symbol` 函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的`Symbol`函数的返回值是不相等的。
│ │ └─ `Symbol` 值也可以转为布尔值,但是不能转为数值。
│ ├── Symbol.prototype.description
│ │ └─ 创建 Symbol 的时候,可以添加一个描述。
│ ├── 作为属性名的 Symbol
│ │ └─ 由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
│ │ └─ Object.defineProperty(a, Symbol(), { value: 'Hello!' });
│ │ └─ Symbol 值作为对象属性名时,不能用点运算符 ; 同理,在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中
│ ├── 属性名的遍历
│ │ └─ Symbol 作为属性名,遍历对象的时候,该属性不会出现在`for...in`、`for...of`循环中,也不会被`Object.keys()`、`Object.getOwnPropertyNames()`、`JSON.stringify()`返回。
│ │ └─ 但是,它也不是私有属性,有一个`Object.getOwnPropertySymbols()`方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
│ │ └─
│ │ └─
│ ├── Symbol.for(),Symbol.keyFor()
│ │ └─ Symbol.for()
│ │ └─ 有时,我们希望重新使用同一个 Symbol 值,`Symbol.for()`方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局。
│ │ └─ `Symbol.for()`与`Symbol()`这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。`Symbol.for()`不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的`key`是否已经存在,如果不存在才会新建一个值。比如,如果你调用`Symbol.for("cat")`30 次,每次都会返回同一个 Symbol 值,但是调用`Symbol("cat")`30 次,会返回 30 个不同的 Symbol 值。
│ │ └─ Symbol.keyFor()
│ │ └─ `Symbol.keyFor()`方法返回一个已登记的 Symbol 类型值的`key`
│ │ └─ `Symbol.for()`为 Symbol 值登记的名字,是全局环境的,不管有没有在全局环境运行。
│ ├── 实例:模块的 Singleton 模式
│ │ └─ Singleton 模式指的是调用一个类,任何时候返回的都是同一个实例。
│ ├── 内置的 Symbol 值
│ │ └─ 除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。
│ │ └─ Symbol.hasInstance
│ │ └─ 对象的`Symbol.hasInstance`属性,指向一个内部方法。当其他对象使用`instanceof`运算符,判断是否为该对象的实例时,会调用这个方法。比如,`foo instanceof Foo`在语言内部,实际调用的是`Foo[Symbol.hasInstance](foo)`。
│ │ └─ Symbol.isConcatSpreadable
│ │ └─ 对象的`Symbol.isConcatSpreadable`属性等于一个布尔值,表示该对象用于`Array.prototype.concat()`时,是否可以展开。
│ │ └─ 数组的默认行为是可以展开,`Symbol.isConcatSpreadable`默认等于`undefined`。该属性等于`true`时,也有展开的效果。
│ │ └─ 类似数组的对象正好相反,默认不展开。它的`Symbol.isConcatSpreadable`属性设为`true`,才可以展开。
│ │ └─ Symbol.species
│ │ └─ 对象的`Symbol.species`属性,指向一个构造函数。创建衍生对象时,会使用该属性。
│ │ └─ 子类`MyArray`继承了父类`Array`,`a`是`MyArray`的实例,`b`和`c`是`a`的衍生对象。你可能会认为,`b`和`c`都是调用数组方法生成的,所以应该是数组(`Array`的实例),但实际上它们也是`MyArray`的实例。
│ │ └─ 由于定义了`Symbol.species`属性,创建衍生对象时就会使用这个属性返回的函数,作为构造函数。这个例子也说明,定义`Symbol.species`属性要采用`get`取值器。
│ │ └─ `Symbol.species`的作用在于,实例对象在运行过程中,需要再次调用自身的构造函数时,会调用该属性指定的构造函数。它主要的用途是,有些类库是在基类的基础上修改的,那么子类使用继承的方法时,作者可能希望返回基类的实例,而不是子类的实例。
│ │ └─ Symbol.match
│ │ └─ 对象的`Symbol.match`属性,指向一个函数。当执行`str.match(myObject)`时,如果该属性存在,会调用它,返回该方法的返回值。
│ │ └─ Symbol.replace
│ │ └─ 对象的`Symbol.replace`属性,指向一个方法,当该对象被`String.prototype.replace`方法调用时,会返回该方法的返回值。
│ │ └─ Symbol.search
│ │ └─ 对象的`Symbol.search`属性,指向一个方法,当该对象被`String.prototype.search`方法调用时,会返回该方法的返回值。
│ │ └─ Symbol.split
│ │ └─ 对象的`Symbol.split`属性,指向一个方法,当该对象被`String.prototype.split`方法调用时,会返回该方法的返回值。
│ │ └─ Symbol.iterator
│ │ └─ 对象的`Symbol.iterator`属性,指向该对象的默认遍历器方法。
│ │ └─ Symbol.toPrimitive
│ │ └─ 对象的`Symbol.toPrimitive`属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
│ │ └─ `Symbol.toPrimitive`被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。
│ │ └─ - Number:该场合需要转成数值 - String:该场合需要转成字符串 - Default:该场合可以转成数值,也可以转成字符串
│ │ └─ Symbol.toStringTag
│ │ └─ 对象的`Symbol.toStringTag`属性,指向一个方法。在该对象上面调用`Object.prototype.toString`方法时,如果这个属性存在,它的返回值会出现在`toString`方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制`[object Object]`或`[object Array]`中`object`后面的那个字符串。