Symbol
基本数据类型。
Symbol() 函数会返回 symbol
类型的值,具有静态属性和静态方法。
作为构造函数并不完整,不支持语法:new Symbol()
Symbol() 返回的 symbol 值都是唯一的。
仅有目的:作为对象属性的标识符。
属性
Symbol.asyncIterator
@@asyncIterator | ❌ IE 不支持
Symbol.asyncIterator
指定一个对象的默认异步迭代器。如果一个对象设置了这个属性,就是异步可迭代对象,可用 for await ... of
循环
Symbol.prototype.description
description 返回 Symbol 对象的可选描述的字符串
Symbol('description').toString()
// "Symbol(description)"
Symbol('description').description
// "description"
Symbol('').description
// ""
Symbol().description
// undefined
Symbol.iterator.toString()
// "Symbol(Symbol.iterator)"
Symbol.iterator.description
// "Symbol.iterator"
Symbol.for('foo').toString()
// "Symbol(foo)"
Symbol.for('foo').description
// "foo"
Symbol.hasInstance
Symbol.hasInstance 用于判断某对象是否为某构造器的实例。 因此可以用它自定义 instanceof 操作符在某个类上的行为。
class MyArray {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
console.log([] instanceof MyArray)
// true
Symbol.hasInstance 属性的属性特性:
writable: false
,
enumerable: false
,
configurable: false
Symbol.isConcatSpreadable
Symbol.isConcatSpreadable 符号用于配置某对象作为 Array.prototype.concat() 方法的参数时是否展开其数组元素。
@@isConcatSpreadable 可以直接定义为对象属性或继承而来,布尔类型。
可以控制数组或类数组的对象的行为,如下
const alpha = ['a', 'b', 'c']
const numeric = [1, 2, 3]
let alphaNumeric = alpha.concat(numeric)
alphaNumeric
// (6) ["a", "b", "c", 1, 2, 3]
numeric[Symbol.isConcatSpreadable] = false
// false
alphaNumeric = alpha.concat(numeric)
// (4) ["a", "b", "c", Array(3)]
Symbol.iterator
Symbol.iterator 为每一个对象定义了默认的迭代器,可以被 for...of 循环使用
一些内置类型拥有默认的迭代器行为,其他类型(如:Object)则没有
Array.prototype[@@iterator]()
TypedArray.prototype[@@iterator]()
String.prototype[@@iterator]()
Map.prototype[@@iterator]()
Set.prototype[@@iterator]()
let myIterable = {}
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
}
// ƒ* () {
// yield 1;
// yield 2;
// yield 3;
// }
console.log([...myIterable])
// (3) [1, 2, 3]
Symbol.match
Symbol.match 指定了匹配的是正则表达式而不是字符串。 String.prototype.match() 会调用此函数。
还用于标识对象是否具有正则表达式行为。
如:String.prototype.startsWith(),String.prototype.endsWith() 和 String.prototype.includes() 会检查第一个参数是否是正则表达式,是则会抛出 TypeError。
但是如果将 Symbol.match 设置为 false,则使用 match 属性的表达式检查会认为该对象不是正则表达式对象,不会抛出 TypeError
const reg = /foo/
'/foo/'.startsWith(reg)
// VM12157:1 Uncaught TypeError: First argument to String.prototype.startsWith must not be a regular expression at String.startsWith (<anonymous>) at <anonymous>:1:9 (anonymous) @ VM12157:1
reg[Symbol.match] = false
// false
'/foo/'.startsWith(reg)
// true
'/bar/'.endsWith(reg)
// false
Symbol.matchAll ??
Symbol.matchAll 返回一个迭代器,根据字符串生成正则表达式的匹配项。String.prototype.matchAll() 方法调用此函数。
'abc'.matchAll(/a/)
// VM282:1 Uncaught TypeError: String.prototype.matchAll called with a non-global RegExp argument at String.matchAll (<anonymous>) at <anonymous>:1:7 (anonymous) @ VM282:1
/a/[Symbol.matchAll]('abc')
// RegExpStringIterator {}
Symbol.replace
Symbol.replace 指定了当一个字符串替换所匹配字符串时所调用的方法。 String.prototype.replace() 会调用此方法。
详见 RegExp.prototype[@@replace]() 和 String.prototype.replace()
class Replace1 {
constructor(value) {
this.value = value;
}
[Symbol.replace](string) {
return `s/${string}/${this.value}/g`;
}
}
'foo'.replace(new Replace1('bar'))
// "s/foo/bar/g"
Symbol.search
Symbol.search 指定了一个搜索方法,接受用户输入的正则表达式,返回该正则表达式在字符串中匹配到的下标。 String.prototype.search() 调用此方法。
详见 RegExp.prototype@@search 和String.prototype.search().
class caseInsensitiveSearch {
constructor(value) {
this.value = value.toLowerCase();
}
[Symbol.search](string) {
return string.toLowerCase().indexOf(this.value);
}
}
'foobar'.search(new caseInsensitiveSearch('BaR'))
// 3
Symbol.species
Symbol.species 是个函数值属性,其被构造函数用以创建派生对象。
在扩展数组类 MyArray 上返回 Array 对象。
例如:当使用例如 map() 这样的方法返回默认的构造函数时,你希望这些方法能够返回父级的 Array 对象,以取代 MyArray 对象
class MyArray extends Array {
// 覆盖 species 到父级的 Array 构造函数上
static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);
console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array); // true
Symbol.split
Symbol.split 指向和一个正则表达式的索引处分割字符串的方法。 String.prototype.split() 调用
class Split1 {
constructor(value) {
this.value = value;
}
[Symbol.split](string) {
const index = string.indexOf(this.value);
return `${this.value}${string.substr(0, index)}/${string.substr(index + this.value.length)}`;
}
}
console.log('foobar'.split(new Split1('foo')));
// "foo/bar"
Symbol.toPrimitive
Symbol.toPrimitive 是一个内置的 Symbol 值,作为对象的函数值属性存在,当一个对象转换为对应的原始值时,会调用此函数。
const object1 = {
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
return 42;
} else if (hint === 'string') {
return '123';
} else {
return null;
}
}
};
console.log(+object1, `${object1}456`, !object1)
// 42 "123456" false
Symbol.toStringTag
Symbol.toStringTag 是一个内置 symbol,通常作为对象的属性键使用,对应的属性值应该为字符串类型,这个字符串用来表示该对象的自定义类型标签,通常只有内置的 Object.prototype.toString() 方法会去读取这个标签并把它包含在自己的返回值里。
class ValidatorClass {
get [Symbol.toStringTag]() {
return "Validator";
}
}
Object.prototype.toString.call(new ValidatorClass())
// "[object Validator]"
Symbol.unscopables
Symbol.unscopables 用于指定对象值,其对象自身和继承的从关联对象的 with 环境绑定中排除的属性名称
const object1 = {
property1: 42
};
with(object1) {
console.log(property1)
}
// 42
object1[Symbol.unscopables] = {
property1: true
};
// {property1: true}
with(object1) {
console.log(property1)
}
// Uncaught ReferenceError: property1 is not defined
方法
Symbol.for()
Symbol.for(key) 根据给定的 key,从运行时的 symbol 注册表中找到对应的 symbol,如果找到了,则返回它,否则会新建一个与该键关联的 symbol,并放入全局 symbol 注册表中
Symbol.for("foo"); // 创建一个 symbol 并放入 symbol 注册表中,键为 "foo"
Symbol.for("foo"); // 从 symbol 注册表中读取键为"foo"的 symbol
Symbol.for("bar") === Symbol.for("bar"); // true,证明了上面说的
Symbol("bar") === Symbol("bar"); // false,Symbol() 函数每次都会返回新的一个 symbol
var sym = Symbol.for("mario");
sym.toString();
// "Symbol(mario)",mario 既是该 symbol 在 symbol 注册表中的键名,又是该 symbol 自身的描述字符串
Symbol.keyFor()
Symbol.keyFor(sym) 方法用来获取全局symbol 注册表中与某个 symbol 关联的键。
如果全局注册表中查找到该 symbol,则返回该 symbol 的 key 值,返回值为字符串类型。否则返回 undefined
// 创建一个全局 Symbol
var globalSym = Symbol.for("foo");
Symbol.keyFor(globalSym); // "foo"
var localSym = Symbol();
Symbol.keyFor(localSym); // undefined,
// 以下Symbol不是保存在全局Symbol注册表中
Symbol.keyFor(Symbol.iterator) // undefined
Symbol.prototype.toString()
toString() 方法返回当前 symbol 对象的字符串表示。
Symbol 对象拥有自己的 toString 方法,因而遮蔽了原型链上的 Object.prototype.toString()。
symbol 原始值不能转换为字符串,所以只能先转换成它的包装对象,再调用 toString() 方法
Symbol("foo") + "bar";
// TypeError: Can't convert symbol to string
Symbol("foo").toString() + "bar"
// "Symbol(foo)bar",就相当于下面的:
Object(Symbol("foo")).toString() + "bar"
// "Symbol(foo)bar"
Symbol.prototype.valueOf()
valueOf() 方法返回当前 symbol 对象所包含的 symbol 原始值。
在 JavaScript 中,虽然大多数类型的对象在某些操作下都会自动的隐式调用自身的 valueOf() 方法或者 toString() 方法来将自己转换成一个原始值,但 symbol 对象不会这么干,symbol 对象无法隐式转换成对应的原始值
Object(Symbol("foo")) + "bar";
// TypeError: can't convert symbol object to primitive
// 无法隐式的调用 valueOf() 方法
Object(Symbol("foo")).valueOf() + "bar";
// TypeError: can't convert symbol to string
// 手动调用 valueOf() 方法,虽然转换成了原始值,但 symbol 原始值不能转换为字符串
Object(Symbol("foo")).toString() + "bar";
// "Symbol(foo)bar",需要手动调用 toString() 方法才行