ES2018新特性学习 中又回顾到了 symbol 数据类型。Symbol 作为一种原始数据类型,除了其 Symbol.iterator 属性和 Symbol.asyncIterator 属性为数据提供 for...of 和 for...await...of 访问机制外,它还有什么功能呢?或者说,ES6 中增加 Symbol 数据类型主要面对什么场景呢?
Symbol 简介
Symbol() 函数返回 symbol 类型的值,该类型具有静态属性和静态方法,并且不支持 new Symbol() 语法。每个从 Symbol() 函数中返回的 symbol 值都是唯一的。一个 symbol 值能作为对象属性的标识符,这是该数据类型最大的目的。
Symbol vs symbol
- Symbol 是一个不支持 new 操作符的函数,用于创建 symbol 类型的值。
- symbol 是一种基本数据类型。目前 JavaScript 支持的 7 种数据类型是:undefined、null、Boolean、String、Number、Object、symbol。
Symbol 使用
我们可以直接使用 Symbol() 函数创建 symbol 类型,并且用一个字符串作为其描述,每次都会创建一个新的 symbol 类型。
1 | let a = Symbol() |
Symbol() 函数不能使用 new 操作符。因为 JavaScript 中 new 操作符用来创建对象,Symbol 生成的是一个原始类型的值,并不是对象。通过原始数据类型创建一个显式包装器对象的方式从 ECMAScript 6 开始不再被支持。 然而现有的原始包装器对象,如 new Boolean()、new String() 以及 new Number() 因为遗留原因仍可被创建。
1 | new Symbol() // TypeError: Symbol is not a constructor at new Symbol |
Symbol 可以接收字符串或者对象作为参数,如果参数是对象的话,Symbol 会调用该对象的 toString() 方法,将其转换为字符串,再生成 symbol 值。
1 | let a = { |
Symbol 值不能与其它数据类型的值进行运算,但是 Symbol 值可以显式转换为字符串或者 Boolean,其它类型的转换都会报 TypeError 错误。
1 | let a = Symbol('World') |
每一个 Symbol 函数生成的值都不相等,因此 Symbol 可以作为标识符,当做对象属性名,这样就可以保证不会出现相同的属性名。这可以有效避免属性被覆盖。
1 | let a = Symbol() |
注意,Symbol 值作为对象属性名时,不能用点运算符,因为点运算符后面是字符串,而 symbol 值并不是字符串。
1 | let a = Symbol() |
在对象内部使用 Symbol 的时候,必须放在方括号中。
1 | let a = Symbol() |
Symbol 属性
Symbol.length
1 | Symbol.length // 0 |
Symbol.iterator
返回对象默认迭代器方法,使用 for...of 进行迭代。
1 | const iterable = { |
Symbol.asyncIterator
返回对象默认的异步迭代器的方法,使用 for await of 进行迭代。
1 | const myAsyncIterator = { |
Symbol.match
指定了匹配的是正则表达式而不是字符串。String.prototype.match() 方法会调用此函数。此函数还用于标识对象是否具有正则表达式的行为。比如: String.prototype.startsWith(),String.prototype.endsWith() 和 String.prototype.includes() 这些方法会检查其第一个参数是否是正则表达式,是正则表达式就抛出一个 TypeError。现在,如果 match symbol 设置为 false(或者一个 假值),就表示该对象不打算用作正则表达式对象。
1 | var re = /foo/ |
Symbol.replace,Symbol.search、Symbol.split 使用方法都与 Symbol.match 比较类似,这里就不赘述了。
Symbol.hasInstance
用于判断某对象是否为某构造器的实例,因此你可以用它自定义 instanceof 操作符在某个类上的行为。
1 | class MyArray { |
Symbol.isConcatSpreadable
用于配置某对象作为 Array.prototype.concat() 方法的参数时是否展开其数组元素。
对于数组对象,默认情况下,用于 concat 时,会按数组元素展开然后进行连接(数组元素作为新数组的元素)。
1 | var arr1 = ['a', 'b', 'c'] |
重置 Symbol.isConcatSpreadable 可以改变默认行为。
1 | var arr1 = ['a', 'b', 'c'] |
对于类似数组的对象,默认是不展开的,如果期望使用 concat 时,展开其元素用于连接,重置 Symbol.isConcatSpreadable 为 true。
1 | var arr1 = [1, 2, 3] |
Symbol.unscopables
Symbol.species
Symbol.toPrimitive
Symbol.toStringTag
Symbol 方法
Symbol.for(key)
使用给定的 key 搜索现有的 symbol,如果找到则返回该 symbol。否则将使用给定的 key 在全局 symbol 注册表中创建一个新的 symbol。
1 | Symbol.for('foo') // 创建一个 symbol 并放入 symbol 注册表中,键为 'foo' |
Symbol.keyFor(sym)
用来获取 symbol 注册表中与某个 symbol 关联的键。
1 | // 创建一个 symbol 并放入 Symbol 注册表,key 为 'foo' |