网站
引入
JS 中有时需要一个绝对不重复的值来当作标识,比如给对象加一个不会冲突的属性名。
字符串可能重复,数字也可能重复,Symbol 就是为了解决这个问题而设计的,每次调用都会生成一个全新的、唯一的值。
正文
定义
定义
Symbol是 ES6 引入的值类型,每次调用Symbol()都会创建一个全局唯一的值,常用作对象的属性标识。
创建 Symbol
使用 Symbol() 创建。
let id1 = Symbol();
let id2 = Symbol();
console.log(id1 === id2);输出结果:
false每次调用 Symbol() 都生成一个全新的值,即使不传任何参数,两个 Symbol 也不相等。
添加描述
Symbol() 可以接受一个字符串作为描述,但描述不影响唯一性。
let id1 = Symbol("id");
let id2 = Symbol("id");
console.log(id1 === id2);输出结果:
false即使描述完全相同,两个 Symbol 依然不相等。
描述只用于调试时辨认:
let id = Symbol("userId");
console.log(id.toString());输出结果:
"Symbol(userId)"typeof 检查
let id = Symbol("id");
console.log(typeof id);输出结果:
"symbol"用作对象属性名
Symbol 最常见的用途是作为对象的属性名,避免属性名冲突。
let id = Symbol("id");
let user = {
name: "Tom",
[id]: 10001
};
console.log(user[id]);
console.log(user.name);输出结果:
10001
"Tom"用中括号 [id] 把 Symbol 作为属性名,外面用 user[id] 来访问。
如果使用字符串做属性名,可能会和其他代码冲突:
let user = {
name: "Tom",
id: 10001
};如果有另一段代码也往 user 上加 id 属性,就会覆盖。用 Symbol 就不会有这个问题。
Symbol 属性不会被 for…in 遍历到
let id = Symbol("id");
let user = {
name: "Tom",
age: 18,
[id]: 10001
};
for (let key in user) {
console.log(key);
}输出结果:
"name"
"age"Symbol 属性不会出现在 for…in 循环中。
同样,Object.keys() 也不会返回 Symbol 属性:
console.log(Object.keys(user));输出结果:
["name", "age"]如果想获取对象上的 Symbol 属性,需要用 Object.getOwnPropertySymbols():
let symbols = Object.getOwnPropertySymbols(user);
console.log(symbols);输出结果:
[Symbol(id)]Symbol.for() 共享 Symbol
Symbol() 每次调用都创建新值,而 Symbol.for() 会根据描述查找已有的 Symbol。
let id1 = Symbol.for("id");
let id2 = Symbol.for("id");
console.log(id1 === id2);输出结果:
trueSymbol.for() 会在全局 Symbol 注册表中查找,如果描述相同的 Symbol 已经存在,就返回已有的那个。
Symbol.keyFor()
Symbol.keyFor() 可以获取通过 Symbol.for() 创建的 Symbol 的描述。
let id = Symbol.for("userId");
console.log(Symbol.keyFor(id));输出结果:
"userId"注意 Symbol.keyFor() 只对 Symbol.for() 创建的 Symbol 有效:
let id = Symbol("userId");
console.log(Symbol.keyFor(id));输出结果:
undefined内置 Symbol
JS 内置了一些 Symbol,用于自定义对象的内部行为。
| 内置 Symbol | 作用 |
|---|---|
Symbol.iterator | 定义对象的迭代行为 |
Symbol.toPrimitive | 定义对象转原始值的行为 |
Symbol.toStringTag | 自定义 Object.prototype.toString 的标签 |
let list = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
let data = this.data;
return {
next() {
if (index < data.length) {
return { value: data[index++], done: false };
}
return { done: true };
}
};
}
};
for (let item of list) {
console.log(item);
}输出结果:
1
2
3Symbol 不能参与运算
Symbol 不能和数字、字符串等进行运算。
let id = Symbol("id");
console.log(id + 1);这种写法会报错。
console.log(id + "text");这种写法也会报错。
如果需要转成字符串,要显式调用:
let id = Symbol("id");
console.log(id.toString());
console.log(String(id));输出结果:
"Symbol(id)"
"Symbol(id)"常见写法
| 写法 | 作用 |
|---|---|
Symbol() | 创建唯一 Symbol |
Symbol("描述") | 创建带描述的 Symbol |
Symbol.for("key") | 创建或获取共享 Symbol |
Symbol.keyFor(sym) | 获取共享 Symbol 的描述 |
obj[sym] | 用 Symbol 作为属性名访问值 |
Object.getOwnPropertySymbols(obj) | 获取对象上所有 Symbol 属性 |
例子
防止属性名冲突
let version = Symbol("version");
let config = {
host: "localhost",
port: 3000,
[version]: "1.0.0"
};
console.log(config[version]);
console.log(Object.keys(config));输出结果:
"1.0.0"
["host", "port"]Symbol 属性不会影响普通的键名遍历。
使用共享 Symbol
let role = Symbol.for("userRole");
let user1 = { name: "Tom", [role]: "admin" };
let user2 = { name: "Jerry", [role]: "user" };
console.log(user1[role]);
console.log(user2[role]);
console.log(user1[Symbol.for("userRole")]);输出结果:
"admin"
"user"
"admin"通过 Symbol.for() 在不同地方使用同一个 Symbol 标识。
特点
Symbol()每次调用都创建唯一值- 描述只是为了调试方便,不影响唯一性
Symbol.for()可以创建共享的 Symbol- Symbol 常用作对象的属性名,避免冲突
- Symbol 属性不会出现在
for…in和Object.keys()中 - Symbol 不能参与运算,转字符串要显式调用
typeof对 Symbol 返回"symbol"
理解
可以把 Symbol 理解成每台机器自动生成的序列号。即使你在两台机器上写同样的描述,序列号也不一样,保证全局唯一。
它最常见的用途就是给对象加一个”不会和别人撞名”的属性。比如你写了一个库,往用户传入的对象上加内部数据,用 Symbol 做属性名就不用担心覆盖用户自己的属性。
日常开发中 Symbol 用得不是特别多,但在写框架、库或者需要定义对象内部行为时会很有用。
引出
理解了 Symbol 后,可以继续学习 JS 引用类型,了解与值类型相对的引用类型。
也可以学习 JS 对象,看看 Symbol 在实际对象中是怎么使用的。