Open mowatermelon opened 4 years ago
使用给定的key
搜索现有的symbol
,如果找到则返回该symbol
。否则将使用给定的key
在全局symbol
注册表中创建一个新的symbol
。
和 Symbol()
不同的是,用 Symbol.for()
方法创建的的 symbol
会被放入一个全局 symbol
注册表中。
Symbol.for()
并不是每次都会创建一个新的 symbol
,它会首先检查给定的 key
是否已经在注册表中了。假如是,则会直接返回上次存储的那个。否则,它会再新建一个。
字段名 | 字段值 |
---|---|
[[key]] | 一个字符串,用来标识每个 symbol |
[[symbol]] | 存储的 symbol 值 |
const { log } = console;
log(Symbol.for("foo")); // Symbol(foo) 创建一个 symbol 并放入 symbol 注册表中,键为 "foo"
log(Symbol.for("foo")); // Symbol(foo) 从 symbol 注册表中读取键为"foo"的 symbol
log(Symbol.for("bar") === Symbol.for("bar")); // true,证明了上面说的
log(Symbol("bar") === Symbol("bar")); // false,Symbol() 函数每次都会返回新的一个 symbol
const sym = Symbol.for("mario");
log(sym.toString());// "Symbol(mario)"
// mario 既是该 symbol 在 symbol 注册表中的键名,又是该 symbol 自身的描述字符串
Symbol.keyFor(sym)
方法用来获取 symbol
注册表中与某个 symbol
关联的键。
如果全局注册表中查找到该symbol
,则返回该symbol
的key
值,形式为string
。如果symbol
未在注册表中,返回undefined
const { log } = console;
// 创建一个 symbol 并放入 Symbol 注册表,key 为 "foo"
const globalSym = Symbol.for("foo");
log(Symbol.keyFor(globalSym)); // "foo"
// 创建一个 symbol,但不放入 symbol 注册表中
const localSym = Symbol();
log(Symbol.keyFor(localSym)); // undefined,所以是找不到 key 的
// well-known symbol 们并不在 symbol 注册表中
log(Symbol.keyFor(Symbol.iterator)); // undefined
Symbol.iterator
一个返回一个对象默认迭代器的方法。被 for...of
使用。
Symbol.iterator
属性的属性特性:
属性特性 | 属性特性值 |
---|---|
writable | false |
enumerable | false |
configurable | false |
const { log } = console;
const myIterable = {
[Symbol.iterator] : async function*() {
yield "hello";
yield "async";
yield "iteration!";
}
};
log([...myIterable]);// [1, 2, 3]
如果一个迭代器 @@iterator
没有返回一个迭代器对象,那么它就是一个不符合标准的迭代器,这样的迭代器将会在运行期抛出异常,甚至非常诡异的 Bug。
const { log } = console;
const nonWellFormedIterable = {
[Symbol.iterator] : () => 1
};
log([...nonWellFormedIterable] );// TypeError: Result of the Symbol.iterator method is not an object
Symbol.asyncIterator
Symbol.asyncIterator
属性的属性特性:
属性特性 | 属性特性值 |
---|---|
writable | false |
enumerable | false |
configurable | false |
一个返回对象默认的异步迭代器
的方法,被 for await of
使用。
目前没有默认设定了[Symbol.asyncIterator]属性的JavaScript
内建的对象。不过,WHATWG
(网页超文本应用技术工作小组)Streams会被设定为第一批异步可迭代对象,[Symbol.asyncIterator] 最近已在设计规范中落地。
目前TS
中未支持Symbol.asyncIterator
。
const { log } = console;
const myAsyncIterable = {
[Symbol.asyncIterator] : async function*() {
yield "hello";
yield "async";
yield "iteration!";
}
};
(async () => {
for await (const x of myAsyncIterable) {
log(x);
// "hello"
// "async"
// "iteration!"
}
})();
Symbol.match
Symbol.match
属性的属性特性:
属性特性 | 属性特性值 |
---|---|
writable | false |
enumerable | false |
configurable | false |
一个用于对字符串进行匹配的方法,也用于确定一个对象是否可以作为正则表达式
使用。被 String.prototype.match()
使用。
比如, String.prototype.startsWith()
,String.prototype.endsWith()
和 String.prototype.includes()
这些方法会检查其第一个参数是否是正则表达式,是正则表达式就抛出一个TypeError
。现在,如果 match symbol
设置为 false
(或者一个 假值
),就表示该对象不打算用作正则表达式
对象。
const { log } = console;
const testStr = 'mowatermelon';
const testObj = { a : 1 };
const reg = /melon/;
const matchFn = reg[Symbol.match];
log(matchFn);// function [Symbol.match]() { [native code] }
// Matches a string with this regular expression, and returns an array containing the results of that search.
// const matchFn: (string: string) => RegExpMatchArray | null
log(typeof matchFn);// function
// log(testStr.startsWith(re)); // TypeError: First argument to String.prototype.startsWith must not be a regular expression
reg[Symbol.match] = false;
log(testStr.startsWith(reg)); // false
log(testStr.includes(reg)); // false
log("/melon/".endsWith(reg)); // true
Symbol.replace
Symbol.replace
属性的属性特性:
属性特性 | 属性特性值 |
---|---|
writable | false |
enumerable | false |
configurable | false |
一个替换匹配字符串
的子串的方法. 被 String.prototype.replace()
使用。
const { log } = console;
const reg = /melon/;
const replaceFn = reg[Symbol.replace];
log(replaceFn);
// const replaceFn: {
// (string: string, replaceValue: string): string;
// (string: string, replacer: (substring: string, ...args: any[]) => string): string;
// }
// Replaces text in a string, using this regular expression.
log(typeof replaceFn);// function
const testStr = 'mo';
const testStr2 = 'watermelon';
class Replace1 {
constructor(value) {
this.value = value;
}
[Symbol.replace](str) {
return `${this.value}, ${str}!`;
}
}
log(testStr.replace(new Replace1('hello'), 111));// hello, mo!
log(testStr2.replace(new Replace1('hello'), 111));// hello, watermelon!
Symbol.search
Symbol.search
属性的属性特性:
属性特性 | 属性特性值 |
---|---|
writable | false |
enumerable | false |
configurable | false |
Symbol.search
指定了一个搜索方法,这个方法接受用户输入的正则表达式,返回该正则表达式在字符串中匹配到的下标,这个方法由以下的方法来调用 String.prototype.search()
。
const { log } = console;
const reg = /melon/g;
const searchFn = reg[Symbol.search];
log(searchFn);
// const searchFn: (string: string) => number
// Finds the position beginning first substring match in a regular expression search using this regular expression.
log(typeof searchFn);// function
const testStr = 'mo';
const testStr2 = 'watermelon';
reg[Symbol.search] = function(str){
return `this is ${str}'s rule, [${reg}]`;
}
class Search1 {
constructor(value) {
this.value = value;
}
[Symbol.search](str) {
return `this is ${str}'s rule, [${this.value}]`;
}
}
log(testStr.search(reg));// this is mo's rule, [/melon/g]
log(testStr2.search(reg));// this is watermelon's rule, [/melon/g]
log(testStr.search(new Search1(reg)));// this is mo's rule, [/melon/g]
log(testStr2.search(new Search1(reg)));// this is watermelon's rule, [/melon/g]
Symbol.split
Symbol.split
属性的属性特性:
属性特性 | 属性特性值 |
---|---|
writable | false |
enumerable | false |
configurable | false |
一个在匹配正则表达式
的索引处拆分一个字符串的方法。被 String.prototype.split()
使用。
const { log } = console;
const reg = /melon/g;
const splitFn = reg[Symbol.split];
log(splitFn);
// const splitFn: (string: string, limit?: number | undefined) => string[]
// Returns an array of substrings that were delimited by strings in the original input that match against this regular expression.
// If the regular expression contains capturing parentheses, then each time this regular expression matches, the results (including any undefined results) of the capturing parentheses are spliced.
log(typeof splitFn);// function
const testStr = 'mo';
const testStr2 = 'watermelon';
reg[Symbol.split] = function(str){
return `this is ${str}'s rule, [${reg}]`;
}
class Split1 {
constructor(value) {
this.value = value;
}
[Symbol.split](str) {
return `this is ${str}'s rule, [${this.value}]`;
}
}
log(testStr.split(reg));// this is mo's rule, [/melon/g]
log(testStr2.split(reg));// this is watermelon's rule, [/melon/g]
log(testStr.split(new Split1(reg)));// this is mo's rule, [/melon/g]
log(testStr2.split(new Split1(reg)));// this is watermelon's rule, [/melon/g]
Symbol.hasInstance
Symbol.hasInstance
属性的属性特性:
属性特性 | 属性特性值 |
---|---|
writable | false |
enumerable | false |
configurable | false |
一个确定一个构造器对象
识别的对象是否为它的实例的方法
。被 instanceof
使用。
const { log } = console;
log(String[Symbol.hasInstance]);// ƒ [Symbol.hasInstance]() { [native code] }
log(Number[Symbol.hasInstance] === String[Symbol.hasInstance]);// true
log(Number[Symbol.hasInstance] === String[Symbol.hasInstance]);// true
log(Function[Symbol.hasInstance] === String[Symbol.hasInstance]);// true
log(Boolean[Symbol.hasInstance] === String[Symbol.hasInstance]);// true
log(Array[Symbol.hasInstance] === String[Symbol.hasInstance]);// true
log(Object[Symbol.hasInstance] === String[Symbol.hasInstance]);// true
class MyArray {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
log([] instanceof MyArray); // true
Symbol.isConcatSpreadable
Symbol.isConcatSpreadable
属性的属性特性:
属性特性 | 属性特性值 |
---|---|
writable | false |
enumerable | false |
configurable | false |
@@isConcatSpreadable
符号 (Symbol.isConcatSpreadable
) 可以直接定义为对象属性或继承而来,它是布尔类型。它可以控制数组或类似数组(array-like
)的对象的行为:
对于数组对象,默认情况下,用于concat
时,会按数组元素展开然后进行连接(数组元素作为新数组的元素)。
重置Symbol.isConcatSpreadable
可以改变默认行为。
对于类似数组
的对象,用于concat
时,该对象整体作为新数组的元素,重置Symbol.isConcatSpreadable
可改变默认行为。
const { log } = console;
const arr1 = [1,2,3];
const arr2 = [4,5,6];
// A Boolean value that if true indicates that an object should flatten to its array elements by Array.prototype.concat.
log(arr1.concat(arr2));// [1, 2, 3, 4, 5, 6]
arr2[Symbol.isConcatSpreadable] = false;
log(arr1.concat(arr2));// [1, 2, 3, Array(3)]
Symbol.unscopables
Symbol.unscopables
属性的属性特性:
属性特性 | 属性特性值 |
---|---|
writable | false |
enumerable | false |
configurable | false |
可以在任何对象上定义 @@unscopables symbol (Symbol.unscopables)
,用于排除属性名称并与 with
环境绑定在一起作为词法变量公开。
请注意,如果使用 Strict mode
,语句将不可用,并且可能也不需要 symbol
。
在 unscopables
对象上设置属性为 true
,将使其 unscopable
并且因此该属性也将不会在词法环境变量中出现。 如果设置属性为 false
,则将使其可 scopable
并且该属性会出现在词法环境变量中。
const { log } = console;
const keys = [];
with(Array.prototype) {
keys.push("something");
}
Object.keys(Array.prototype[Symbol.unscopables]);
// ["copyWithin", "entries", "fill", "find", "findIndex", "includes", "keys", "values"]
// 也可以为你自己的对象设置 `unscopables`
const obj = {
foo: 1,
bar: 2
};
obj[Symbol.unscopables] = {
foo: false,
bar: true
};
with(obj) {
log(foo); // 1
log(bar); // ReferenceError: bar is not defined
}
Symbol.species
Symbol.species
属性的属性特性:
属性特性 | 属性特性值 |
---|---|
writable | false |
enumerable | false |
configurable | false |
Symbol.species
是个函数值属性,其被构造函数用以创建派生对象。species
访问器属性允许子类覆盖对象的默认构造函数。
const { log } = console;
class MyArray extends Array {
// 覆盖 species 到父级的 Array 构造函数上
static get [Symbol.species]() { return Array; }
}
const a = new MyArray(1,2,3);
const mapped = a.map(x => x * x);
log(mapped instanceof MyArray); // false
log(mapped instanceof Array); // true
Symbol.toPrimitive
Symbol.toPrimitive
属性的属性特性:
属性特性 | 属性特性值 |
---|---|
writable | false |
enumerable | false |
configurable | false |
Symbol.toPrimitive
是一个内置的 Symbol
值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值
时,会调用此函数。
const { log } = console;
// 一个没有提供 Symbol.toPrimitive 属性的对象,参与运算时的输出结果
const obj1 = {};
log(+obj1); // NaN
log(`${obj1}`); // "[object Object]"
log(obj1 + ""); // "[object Object]"
// 接下面声明一个对象,手动赋予了 Symbol.toPrimitive 属性,再来查看输出结果
const obj2 = {
[Symbol.toPrimitive](hint) {
if (hint == "number") {
return 10;
}
if (hint == "string") {
return "hello";
}
return true;
}
};
log(+obj2); // 10 -- hint 参数值是 "number"
log(`${obj2}`); // "hello" -- hint 参数值是 "string"
log(obj2 + ""); // "true" -- hint 参数值是 "default"
Symbol.toStringTag
Symbol.toStringTag
属性的属性特性:
属性特性 | 属性特性值 |
---|---|
writable | false |
enumerable | false |
configurable | false |
Symbol.toStringTag
是一个内置 symbol
,它通常作为对象的属性键
使用,对应的属性值
应该为字符串
类型。
这个字符串用来表示该对象的自定义类型
标签,通常只有内置的 Object.prototype.toString()
方法会去读取这个标签并把它包含在自己的返回值
里。
许多内置的 JavaScript
对象类型即便没有 toStringTag
属性,也能被 toString()
方法识别并返回特定的类型标签。
const { log } = console;
log(Object.prototype.toString.call('foo')); // "[object String]"
log(Object.prototype.toString.call([1, 2])); // "[object Array]"
log(Object.prototype.toString.call(3)); // "[object Number]"
log(Object.prototype.toString.call(true)); // "[object Boolean]"
log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
log(Object.prototype.toString.call(null)); // "[object Null]"
另外一些对象类型则不然,toString()
方法能识别它们是因为引擎为它们设置好了 toStringTag
标签
const { log } = console;
log(Object.prototype.toString.call(new Map())); // "[object Map]"
log(Object.prototype.toString.call(function* () {})); // "[object GeneratorFunction]"
log(Object.prototype.toString.call(Promise.resolve())); // "[object Promise]"
但你自己创建的类不会有这份特殊待遇,toString()
找不到 toStringTag
属性时只好返回默认的 Object
标签,加上 toStringTag
属性,你的类也会有自定义的类型标签了。
const { log } = console;
class ValidatorClass1 {}
log(Object.prototype.toString.call(new ValidatorClass1())); // "[object Object]"
class ValidatorClass2 {
get [Symbol.toStringTag]() {
return "Validator";
}
}
log(Object.prototype.toString.call(new ValidatorClass2())); // "[object Validator]"
标记(Symbol)
自
ECMAScript 2015
起,symbol
成为了一种新的[原生类型][Primitive],就像number
和string
一样。详细说明请查看[MDN Symbol][Symbol]
基础说明
Symbol()
函数会返回symbol
类型的值,该类型具有静态属性
和静态方法
。它的
静态属性
会暴露几个内建
的成员对象;它的静态方法
会暴露全局的symbol
注册,且类似于内建对象类
。上面使用
Symbol()
函数的语法,不会在你的整个代码库中创建一个可用的全局symbol
类型。 要创建跨文件可用的symbol,甚至跨域(每个都有它自己的全局作用域) , 使用Symbol.for()
方法和Symbol.keyFor()
方法从全局的symbol
注册表设置和取得symbol
。但作为
构造函数
来说它并不完整,因为它不支持语法:"new Symbol()
"。这会阻止创建一个显式的
Symbol
包装器对象而不是一个Symbol
值。围绕原始数据类型创建一个显式包装器
对象从ECMAScript 6
开始不再被支持。 然而,现有的原始包装器对象,如new Boolean
、new String
以及new Number
,因为遗留原因仍可被创建。如果你真的想创建一个
Symbol
包装器对象 (Symbol wrapper object
),你可以使用Object()
函数注意事项
String(sym)
conversion 的作用会像symbol
类型调用Symbol.prototype.toString()
一样,但是注意new String(sym)
将抛出异常TypeError: Cannot convert a Symbol value to a string
。sym + 1
,将抛出异常TypeError: Cannot convert a Symbol value to a string
,这会阻止你从一个symbol
值隐式地创建一个新的string
类型的属性名。当使用
JSON.stringify()
时,以symbol
值作为键的属性会被完全忽略:Symbols
在for...in
迭代中不可枚举。另外,Object.getOwnPropertyNames()
不会返回symbol
对象的属性