1
1
2
- # Symbol 类型
2
+ # symbol 类型
3
3
4
- 根据规范,对象的属性键只能是字符串类型或者 Symbol 类型。不是 Number,也不是 Boolean,只有字符串或 Symbol 这两种类型。
4
+ 根据规范,只有两种原始类型可以用作对象属性键:
5
5
6
- 到目前为止,我们只见过字符串。现在我们来看看 Symbol 能给我们带来什么好处。
6
+ - 字符串类型
7
+ - symbol 类型
7
8
8
- ## Symbol
9
+ 否则,如果使用另一种类型,例如数字,它会被自动转换为字符串。所以 ` obj[1] ` 与 ` obj["1"] ` 相同,而 ` obj[true] ` 与 ` obj["true"] ` 相同。
9
10
10
- "Symbol" 值表示唯一的标识符。
11
+ 到目前为止,我们一直只使用字符串。
12
+
13
+ 现在我们来看看 symbol 能给我们带来什么。
14
+
15
+ ## symbol
16
+
17
+ "symbol" 值表示唯一的标识符。
11
18
12
19
可以使用 ` Symbol() ` 来创建这种类型的值:
13
20
14
21
``` js
15
- // id 是 symbol 的一个实例化对象
16
22
let id = Symbol ();
17
23
```
18
24
19
- 创建时,我们可以给 Symbol 一个描述(也称为 Symbol 名),这在代码调试时非常有用:
25
+ 创建时,我们可以给 symbol 一个描述(也称为 symbol 名),这在代码调试时非常有用:
20
26
21
27
``` js
22
- // id 是描述为 "id" 的 Symbol
28
+ // id 是描述为 "id" 的 symbol
23
29
let id = Symbol (" id" );
24
30
```
25
31
26
- Symbol 保证是唯一的。即使我们创建了许多具有相同描述的 Symbol ,它们的值也是不同。描述只是一个标签,不影响任何东西。
32
+ symbol 保证是唯一的。即使我们创建了许多具有相同描述的 symbol ,它们的值也是不同。描述只是一个标签,不影响任何东西。
27
33
28
- 例如,这里有两个描述相同的 Symbol —— 它们不相等:
34
+ 例如,这里有两个描述相同的 symbol —— 它们不相等:
29
35
30
36
``` js run
31
37
let id1 = Symbol (" id" );
@@ -36,23 +42,26 @@ alert(id1 == id2); // false
36
42
*/ ! *
37
43
```
38
44
39
- 如果你熟悉 Ruby 或者其他有 "Symbol" 的语言 —— 别被误导。JavaScript 的 Symbol 是不同的。
45
+ 如果你熟悉 Ruby 或者其他有 "symbol" 的语言 —— 别被误导。JavaScript 的 symbol 是不同的。
46
+
47
+ 所以,总而言之,symbol 是带有可选描述的“原始唯一值”。让我们看看我们可以在哪里使用它们。
40
48
41
- ```` warn header="Symbol 不会被自动转换为字符串"
42
- JavaScript 中的大多数值都支持字符串的隐式转换。例如,我们可以 `alert` 任何值,都可以生效。Symbol 比较特殊,它不会被自动转换。
49
+ ```` warn header="symbol 不会被自动转换为字符串"
50
+ JavaScript 中的大多数值都支持字符串的隐式转换。例如,我们可以 `alert` 任何值,都可以生效。symbol 比较特殊,它不会被自动转换。
43
51
44
52
例如,这个 `alert` 将会提示出错:
45
53
46
54
```js run
47
55
let id = Symbol("id");
48
56
*!*
49
- alert(id); // 类型错误:无法将 Symbol 值转换为字符串。
57
+ alert(id); // 类型错误:无法将 symbol 值转换为字符串。
50
58
*/!*
51
59
```
52
60
53
- 这是一种防止混乱的“语言保护”,因为字符串和 Symbol 有本质上的不同,不应该意外地将它们转换成另一个。
61
+ 这是一种防止混乱的“语言保护”,因为字符串和 symbol 有本质上的不同,不应该意外地将它们转换成另一个。
62
+
63
+ 如果我们真的想显示一个 symbol,我们需要在它上面调用 `.toString()`,如下所示:
54
64
55
- 如果我们真的想显示一个 Symbol,我们需要在它上面调用 `.toString()`,如下所示:
56
65
```js run
57
66
let id = Symbol("id");
58
67
*!*
@@ -61,6 +70,7 @@ alert(id.toString()); // Symbol(id),现在它有效了
61
70
```
62
71
63
72
或者获取 `symbol.description` 属性,只显示描述(description):
73
+
64
74
```js run
65
75
let id = Symbol("id");
66
76
*!*
@@ -72,11 +82,12 @@ alert(id.description); // id
72
82
73
83
## “隐藏”属性
74
84
75
- Symbol 允许我们创建对象的“隐藏”属性,代码的任何其他部分都不能意外访问或重写这些属性。
85
+
86
+ symbol 允许我们创建对象的“隐藏”属性,代码的任何其他部分都不能意外访问或重写这些属性。
76
87
77
88
例如,如果我们使用的是属于第三方代码的 ` user ` 对象,我们想要给它们添加一些标识符。
78
89
79
- 我们可以给它们使用 Symbol 键:
90
+ 我们可以给它们使用 symbol 键:
80
91
81
92
``` js run
82
93
let user = { // 属于另一个代码
@@ -87,12 +98,12 @@ let id = Symbol("id");
87
98
88
99
user[id] = 1 ;
89
100
90
- alert ( user[id] ); // 我们可以使用 Symbol 作为键来访问数据
101
+ alert ( user[id] ); // 我们可以使用 symbol 作为键来访问数据
91
102
```
92
103
93
104
使用 ` Symbol("id") ` 作为键,比起用字符串 ` "id" ` 来有什么好处呢?
94
105
95
- 因为 ` user ` 对象属于其他的代码,那些代码也会使用这个对象,所以我们不应该在它上面直接添加任何字段,这样很不安全。但是你添加的 Symbol 属性不会被意外访问到,第三方代码根本不会看到它,所以使用 Symbol 基本上不会有问题。
106
+ 因为 ` user ` 对象属于其他的代码,那些代码也会使用这个对象,所以我们不应该在它上面直接添加任何字段,这样很不安全。但是你添加的 symbol 属性不会被意外访问到,第三方代码根本不会看到它,所以使用 symbol 基本上不会有问题。
96
107
97
108
另外,假设另一个脚本希望在 ` user ` 中有自己的标识符,以实现自己的目的。这可能是另一个 JavaScript 库,因此脚本之间完全不了解彼此。
98
109
@@ -105,7 +116,7 @@ let id = Symbol("id");
105
116
user[id] = " Their id value" ;
106
117
```
107
118
108
- 我们的标识符和它们的标识符之间不会有冲突,因为 Symbol 总是不同的,即使它们有相同的名字。
119
+ 我们的标识符和它们的标识符之间不会有冲突,因为 symbol 总是不同的,即使它们有相同的名字。
109
120
110
121
……但如果我们处于同样的目的,使用字符串 ` "id" ` 而不是用 symbol,那么 ** 就会** 出现冲突:
111
122
@@ -121,9 +132,9 @@ user.id = "Their id value"
121
132
// 砰!无意中被另一个脚本重写了 id!
122
133
```
123
134
124
- ### 对象字面量中的 Symbol
135
+ ### 对象字面量中的 symbol
125
136
126
- 如果我们要在对象字面量 ` {...} ` 中使用 Symbol ,则需要使用方括号把它括起来。
137
+ 如果我们要在对象字面量 ` {...} ` 中使用 symbol ,则需要使用方括号把它括起来。
127
138
128
139
就像这样:
129
140
@@ -139,9 +150,9 @@ let user = {
139
150
```
140
151
这是因为我们需要变量 ` id ` 的值作为键,而不是字符串 "id"。
141
152
142
- ### Symbol 在 for..in 中会被跳过
153
+ ### symbol 在 for..in 中会被跳过
143
154
144
- Symbol 属性不参与 ` for..in ` 循环。
155
+ symbol 属性不参与 ` for..in ` 循环。
145
156
146
157
例如:
147
158
@@ -154,10 +165,10 @@ let user = {
154
165
};
155
166
156
167
* ! *
157
- for (let key in user) alert (key); // name, age (no symbols)
168
+ for (let key in user) alert (key); // name, age(没有 symbol)
158
169
*/ ! *
159
170
160
- // 使用 Symbol 任务直接访问
171
+ // 使用 symbol 任务直接访问
161
172
alert ( " Direct: " + user[id] );
162
173
```
163
174
@@ -176,76 +187,76 @@ let clone = Object.assign({}, user);
176
187
alert ( clone[id] ); // 123
177
188
```
178
189
179
- 这里并不矛盾,就是这样设计的。这里的想法是当我们克隆或者合并一个 object 时,通常希望 ** 所有** 属性被复制(包括像 ` id ` 这样的 Symbol )。
190
+ 这里并不矛盾,就是这样设计的。这里的想法是当我们克隆或者合并一个 object 时,通常希望 ** 所有** 属性被复制(包括像 ` id ` 这样的 symbol )。
180
191
181
192
## 全局 symbol
182
193
183
- 正如我们所看到的,通常所有的 Symbol 都是不同的,即使它们有相同的名字。但有时我们想要名字相同的 Symbol 具有相同的实体。例如,应用程序的不同部分想要访问的 Symbol ` "id" ` 指的是完全相同的属性。
194
+ 正如我们所看到的,通常所有的 symbol 都是不同的,即使它们有相同的名字。但有时我们想要名字相同的 symbol 具有相同的实体。例如,应用程序的不同部分想要访问的 symbol ` "id" ` 指的是完全相同的属性。
184
195
185
- 为了实现这一点,这里有一个 ** 全局 Symbol 注册表** 。我们可以在其中创建 Symbol 并在稍后访问它们,它可以确保每次访问相同名字的 Symbol 时,返回的都是相同的 Symbol 。
196
+ 为了实现这一点,这里有一个 ** 全局 symbol 注册表** 。我们可以在其中创建 symbol 并在稍后访问它们,它可以确保每次访问相同名字的 symbol 时,返回的都是相同的 symbol 。
186
197
187
- 要从注册表中读取(不存在则创建)Symbol ,请使用 ` Symbol.for(key) ` 。
198
+ 要从注册表中读取(不存在则创建)symbol ,请使用 ` Symbol.for(key) ` 。
188
199
189
- 该调用会检查全局注册表,如果有一个描述为 ` key ` 的 Symbol ,则返回该 Symbol ,否则将创建一个新 Symbol (` Symbol(key) ` ),并通过给定的 ` key ` 将其存储在注册表中。
200
+ 该调用会检查全局注册表,如果有一个描述为 ` key ` 的 symbol ,则返回该 symbol ,否则将创建一个新 symbol (` Symbol(key) ` ),并通过给定的 ` key ` 将其存储在注册表中。
190
201
191
202
例如:
192
203
193
204
``` js run
194
205
// 从全局注册表中读取
195
- let id = Symbol .for (" id" ); // 如果该 Symbol 不存在,则创建它
206
+ let id = Symbol .for (" id" ); // 如果该 symbol 不存在,则创建它
196
207
197
208
// 再次读取(可能是在代码中的另一个位置)
198
209
let idAgain = Symbol .for (" id" );
199
210
200
- // 相同的 Symbol
211
+ // 相同的 symbol
201
212
alert ( id === idAgain ); // true
202
213
```
203
214
204
- 注册表内的 Symbol 被称为 ** 全局 Symbol ** 。如果我们想要一个应用程序范围内的 Symbol ,可以在代码中随处访问 —— 这就是它们的用途。
215
+ 注册表内的 symbol 被称为 ** 全局 symbol ** 。如果我们想要一个应用程序范围内的 symbol ,可以在代码中随处访问 —— 这就是它们的用途。
205
216
206
217
``` smart header="这听起来像 Ruby"
207
- 在一些编程语言中,例如 Ruby,每个名字都有一个 Symbol 。
218
+ 在一些编程语言中,例如 Ruby,每个名字都有一个 symbol 。
208
219
209
- 正如我们所看到的,在 JavaScript 中,全局 Symbol 也是这样的。
220
+ 正如我们所看到的,在 JavaScript 中,全局 symbol 也是这样的。
210
221
```
211
222
212
223
### Symbol.keyFor
213
224
214
- 对于全局 Symbol ,不仅有 ` Symbol.for(key) ` 按名字返回一个 Symbol ,还有一个反向调用:` Symbol.keyFor(sym) ` ,它的作用完全反过来:通过全局 Symbol 返回一个名字。
225
+ 对于全局 symbol ,不仅有 ` Symbol.for(key) ` 按名字返回一个 symbol ,还有一个反向调用:` Symbol.keyFor(sym) ` ,它的作用完全反过来:通过全局 symbol 返回一个名字。
215
226
216
227
例如:
217
228
218
229
``` js run
219
- // 通过 name 获取 Symbol
230
+ // 通过 name 获取 symbol
220
231
let sym = Symbol .for (" name" );
221
232
let sym2 = Symbol .for (" id" );
222
233
223
- // 通过 Symbol 获取 name
234
+ // 通过 symbol 获取 name
224
235
alert ( Symbol .keyFor (sym) ); // name
225
236
alert ( Symbol .keyFor (sym2) ); // id
226
237
```
227
238
228
- ` Symbol.keyFor ` 内部使用全局 Symbol 注册表来查找 Symbol 的键。所以它不适用于非全局 Symbol 。如果 Symbol 不是全局的,它将无法找到它并返回 ` undefined ` 。
239
+ ` Symbol.keyFor ` 内部使用全局 symbol 注册表来查找 symbol 的键。所以它不适用于非全局 symbol 。如果 symbol 不是全局的,它将无法找到它并返回 ` undefined ` 。
229
240
230
- 也就是说,任何 Symbol 都具有 ` description ` 属性。
241
+ 也就是说,任何 symbol 都具有 ` description ` 属性。
231
242
232
243
例如:
233
244
234
245
``` js run
235
246
let globalSymbol = Symbol .for (" name" );
236
247
let localSymbol = Symbol (" name" );
237
248
238
- alert ( Symbol .keyFor (globalSymbol) ); // name,全局 Symbol
249
+ alert ( Symbol .keyFor (globalSymbol) ); // name,全局 symbol
239
250
alert ( Symbol .keyFor (localSymbol) ); // undefined,非全局
240
251
241
252
alert ( localSymbol .description ); // name
242
253
```
243
254
244
- ## 系统 Symbol
255
+ ## 系统 symbol
245
256
246
- JavaScript 内部有很多“系统” Symbol ,我们可以使用它们来微调对象的各个方面。
257
+ JavaScript 内部有很多“系统” symbol ,我们可以使用它们来微调对象的各个方面。
247
258
248
- 它们都被列在了 [ 众所周知的 Symbol ] ( https://tc39.github.io/ecma262/#sec-well-known-symbols ) 表的规范中:
259
+ 它们都被列在了 [ 众所周知的 symbol ] ( https://tc39.github.io/ecma262/#sec-well-known-symbols ) 表的规范中:
249
260
250
261
- ` Symbol.hasInstance `
251
262
- ` Symbol.isConcatSpreadable `
@@ -255,23 +266,24 @@ JavaScript 内部有很多“系统” Symbol,我们可以使用它们来微
255
266
256
267
例如,` Symbol.toPrimitive ` 允许我们将对象描述为原始值转换。我们很快就会看到它的使用。
257
268
258
- 当我们研究相应的语言特征时,我们对其他的 Symbol 也会慢慢熟悉起来。
269
+ 当我们研究相应的语言特征时,我们对其他的 symbol 也会慢慢熟悉起来。
259
270
260
271
## 总结
261
272
262
- ` Symbol ` 是唯一标识符的基本类型
273
+ ` symbol ` 是唯一标识符的基本类型
263
274
264
- Symbol 是使用带有可选描述(name)的 ` Symbol() ` 调用创建的。
275
+ symbol 是使用带有可选描述(name)的 ` Symbol() ` 调用创建的。
265
276
266
- Symbol 总是不同的值,即使它们有相同的名字。如果我们希望同名的 Symbol 相等,那么我们应该使用全局注册表:` Symbol.for(key) ` 返回(如果需要的话则创建)一个以 ` key ` 作为名字的全局 Symbol 。使用 ` Symbol.for ` 多次调用 ` key ` 相同的 Symbol 时,返回的就是同一个 Symbol 。
277
+ symbol 总是不同的值,即使它们有相同的名字。如果我们希望同名的 symbol 相等,那么我们应该使用全局注册表:` Symbol.for(key) ` 返回(如果需要的话则创建)一个以 ` key ` 作为名字的全局 symbol 。使用 ` Symbol.for ` 多次调用 ` key ` 相同的 symbol 时,返回的就是同一个 symbol 。
267
278
268
- Symbol 有两个主要的使用场景:
279
+ symbol 有两个主要的使用场景:
269
280
270
281
1 . “隐藏” 对象属性。
271
- 如果我们想要向“属于”另一个脚本或者库的对象添加一个属性,我们可以创建一个 Symbol 并使用它作为属性的键。Symbol 属性不会出现在 ` for..in ` 中,因此它不会意外地被与其他属性一起处理。并且,它不会被直接访问,因为另一个脚本没有我们的 symbol。因此,该属性将受到保护,防止被意外使用或重写。
272
282
273
- 因此我们可以使用 Symbol 属性“秘密地”将一些东西隐藏到我们需要的对象中,但其他地方看不到它。
283
+ 如果我们想要向“属于”另一个脚本或者库的对象添加一个属性,我们可以创建一个 symbol 并使用它作为属性的键。symbol 属性不会出现在 ` for..in ` 中,因此它不会意外地被与其他属性一起处理。并且,它不会被直接访问,因为另一个脚本没有我们的 symbol。因此,该属性将受到保护,防止被意外使用或重写。
284
+
285
+ 因此我们可以使用 symbol 属性“秘密地”将一些东西隐藏到我们需要的对象中,但其他地方看不到它。
274
286
275
- 2 . JavaScript 使用了许多系统 Symbol ,这些 Symbol 可以作为 ` Symbol.* ` 访问。我们可以使用它们来改变一些内建行为。例如,在本教程的后面部分,我们将使用 ` Symbol.iterator ` 来进行 [ 迭代] ( info:iterable ) 操作,使用 ` Symbol.toPrimitive ` 来设置 [ 对象原始值的转换] ( info:object-toprimitive ) 等等。
287
+ 2 . JavaScript 使用了许多系统 symbol ,这些 symbol 可以作为 ` Symbol.* ` 访问。我们可以使用它们来改变一些内建行为。例如,在本教程的后面部分,我们将使用 ` Symbol.iterator ` 来进行 [ 迭代] ( info:iterable ) 操作,使用 ` Symbol.toPrimitive ` 来设置 [ 对象原始值的转换] ( info:object-toprimitive ) 等等。
276
288
277
- 从技术上说,Symbol 不是 100% 隐藏的。有一个内建方法 [ Object.getOwnPropertySymbols(obj)] ( mdn:js/Object/getOwnPropertySymbols ) 允许我们获取所有的 Symbol 。还有一个名为 [ Reflect.ownKeys(obj)] ( mdn:js/Reflect/ownKeys ) 的方法可以返回一个对象的 ** 所有** 键,包括 Symbol 。所以它们并不是真正的隐藏。但是大多数库、内建方法和语法结构都没有使用这些方法。
289
+ 从技术上说,symbol 不是 100% 隐藏的。有一个内建方法 [ Object.getOwnPropertySymbols(obj)] ( mdn:js/Object/getOwnPropertySymbols ) 允许我们获取所有的 symbol 。还有一个名为 [ Reflect.ownKeys(obj)] ( mdn:js/Reflect/ownKeys ) 的方法可以返回一个对象的 ** 所有** 键,包括 symbol 。所以它们并不是真正的隐藏。但是大多数库、内建方法和语法结构都没有使用这些方法。
0 commit comments