Symbol
是 JavaScript 中的一种基本数据类型,它在 ES6(ECMAScript 2015)中被引入。Symbol
类型的值是唯一的且不可改变的,这使得它们非常适合用作对象属性的键,以避免键名冲突或用于创建私有属性。
创建 Symbol
你可以通过调用 Symbol()
函数来创建一个新的 Symbol
值。每次调用 Symbol()
都会生成一个全新的、独一无二的 Symbol
。
javascript
let sym1 = Symbol();
let sym2 = Symbol("description"); // 可选的描述字符串
- 注意 :即使两个
Symbol
使用相同的描述字符串创建,它们仍然是不同的Symbol
。
Symbol 的特性
唯一性
每个 Symbol
都是唯一的,即使它们有相同的描述:
javascript
let sym3 = Symbol("foo");
let sym4 = Symbol("foo");
console.log(sym3 === sym4); // false
虽然 Symbol
的描述不会影响其唯一性,但它可以在调试时提供信息。例如,当使用 console.log
输出 Symbol
或者在错误消息中显示 Symbol
时,描述可以帮助理解 Symbol
的用途。
javascript
console.log(Symbol("foo")); // Symbol(foo)
作为对象属性键
Symbol
最常见的用途之一是作为对象属性的键。由于 Symbol
的唯一性,可以确保不会与其他属性发生命名冲突,即使这些属性来自第三方代码库。由于每一个 Symbol 值都是不相等的,这意味着只要 Symbol 值作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
javascript
let obj = {};
let sym = Symbol("key");
obj[sym] = "value";
console.log(obj[sym]); // value
console.log(Object.keys(obj)); // []
console.log(Object.getOwnPropertyNames(obj)); // []
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(key)]
js
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
全局符号注册表
有时你可能希望多个 Symbol
在整个应用中是共享的和可识别的。为此,JavaScript 提供了全局符号注册表,可以通过 Symbol.for
和 Symbol.keyFor
来访问。
Symbol.for(key)
:查找已存在的Symbol
或创建一个新的Symbol
并将其存储在全球符号注册表中。Symbol.keyFor(sym)
:返回给定Symbol
的键名(如果它是在全局符号注册表中定义的)。
javascript
let symA = Symbol.for("com.example.id");
let symB = Symbol.for("com.example.id");
console.log(symA === symB); // true
console.log(Symbol.keyFor(symA)); // com.example.id
内置符号
JavaScript 还为一些内置行为定义了特殊的 Symbol
,称为"well-known symbols"。这些符号通常用于指定对象的行为,并允许开发者自定义这些行为。例如:
Symbol.iterator
:用于定义对象的默认迭代行为。Symbol.toPrimitive
:用于定义对象到原始值的转换。Symbol.species
:用于指定构造函数应返回的构造函数。Symbol.hasInstance
:用于自定义instanceof
操作符的行为。- 等等。
Symbol 的不可枚举性
当 Symbol
用作对象属性键时,默认情况下它是不可枚举的,这意味着它不会出现在 for...in
循环中,也不会出现在 Object.keys()
, Object.getOwnPropertyNames()
等方法的结果中。但是,你可以使用 Object.getOwnPropertySymbols()
来获取对象的所有 Symbol
属性。
Symbol()
函数创建 Symbol 值时,可以用参数添加一个描述。
ini
const sym = Symbol('foo');
上面代码中,sym
这个值的描述就是字符串foo
。
但是,读取这个描述需要将 Symbol 显式转为字符串,即下面的写法。
scss
const sym = Symbol('foo');
String(sym) // "Symbol(foo)"
sym.toString() // "Symbol(foo)"
属性名的遍历
Symbol 值作为属性名,遍历对象的时候,该属性不会出现在for...in
、for...of
循环中,也不会被Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回。
但是,它也不是私有属性,有一个Object.getOwnPropertySymbols()
方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
ini
const obj = {};
let a = Symbol('a');
let b = Symbol('b');
obj[a] = 'Hello';
obj[b] = 'World';
const objectSymbols = Object.getOwnPropertySymbols(obj);
objectSymbols
// [Symbol(a), Symbol(b)]
ini
const obj = {};
const foo = Symbol('foo');
obj[foo] = 'bar';
for (let i in obj) {
console.log(i); // 无输出
}
Object.getOwnPropertyNames(obj) // []
Object.getOwnPropertySymbols(obj) // [Symbol(foo)]