You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
By specification, object property keys may be either of string type, or of symbol type. Not numbers, not booleans, only strings or symbols, these two types.
Till now we've been using only strings. Now let's see the benefits that symbols can give us.
6
+
到目前為止,我們一直只用到字串。現在讓我們來看看 Symbol 能給我們帶來哪些好處。
7
7
8
-
## Symbols
8
+
## 符號(Symbol)
9
9
10
-
A "symbol" represents a unique identifier.
10
+
一個 "symbol" 代表唯一的識別符號。
11
11
12
-
A value of this type can be created using `Symbol()`:
12
+
可以用 `Symbol()` 來創建此類型的值:
13
13
14
14
```js
15
-
// id is a new symbol
15
+
// id 是一個新的 Symbol
16
16
let id =Symbol();
17
17
```
18
18
19
-
Upon creation, we can give symbol a description (also called a symbol name), mostly useful for debugging purposes:
19
+
創建後,我們可以給 Symbol 一個敘述(也稱為 Symbol 名稱),在進行除錯時大多很有用處:
20
20
21
21
```js run
22
-
// id is a symbol with the description "id"
22
+
// id 是一個擁有敘述 "id" 的 Symbol
23
23
let id =Symbol("id");
24
24
```
25
25
26
-
Symbols are guaranteed to be unique. Even if we create many symbols with the same description, they are different values. The description is just a label that doesn't affect anything.
26
+
Symbol 保證是唯一的。即使我們創建了許多擁有相同敘述的 Symbol,它們的值還是不同的。敘述只是一個標籤,不影響任何東西。
27
27
28
-
For instance, here are two symbols with the same description -- they are not equal:
28
+
舉例來說,這裡有兩個擁有相同敘述的 Symbol -- 它們並不相等:
29
29
30
30
```js run
31
31
let id1 =Symbol("id");
@@ -36,12 +36,12 @@ alert(id1 == id2); // false
36
36
*/!*
37
37
```
38
38
39
-
If you are familiar with Ruby or another language that also has some sort of "symbols" -- please don't be misguided. JavaScript symbols are different.
````warn header="Symbols don't auto-convert to a string"
42
-
Most values in JavaScript support implicit conversion to a string. For instance, we can `alert` almost any value, and it will work. Symbols are special. They don't auto-convert.
@@ -50,17 +50,18 @@ alert(id); // TypeError: Cannot convert a Symbol value to a string
50
50
*/!*
51
51
```
52
52
53
-
That's a "language guard" against messing up, because strings and symbols are fundamentally different and should not accidentally convert one into another.
53
+
這是一種防止混亂的 "語言防範(language guard)",因為字串與 Symbol 有本質上的不同,不應該意外地將它們互相轉換。
If we really want to show a symbol, we need to explicitly call `.toString()` on it, like here:
56
57
```js run
57
58
let id = Symbol("id");
58
59
*!*
59
-
alert(id.toString()); // Symbol(id), now it works
60
+
alert(id.toString()); // Symbol(id), 現在它可以正常運作了
60
61
*/!*
61
62
```
62
63
63
-
Or get `symbol.description` property to show the description only:
64
+
或是取得 `symbol.description` 屬性來單純顯示敘述:
64
65
```js run
65
66
let id = Symbol("id");
66
67
*!*
@@ -70,33 +71,33 @@ alert(id.description); // id
70
71
71
72
````
72
73
73
-
## "Hidden" properties
74
+
## "隱藏(Hidden)" 屬性
74
75
75
-
Symbols allow us to create "hidden" properties of an object, that no other part of code can accidentally access or overwrite.
76
+
Symbol 允許我們創建物件的 "隱藏" 屬性,其他部分的程式碼都無法意外存取到或是覆寫它。
76
77
77
-
For instance, if we're working with `user`objects, that belong to a third-party code. We'd like to add identifiers to them.
78
+
舉例來說,如果我們正在操作屬於第三方程式碼的 `user`物件們。我們想要增加識別符到它們上。
78
79
79
-
Let's use a symbol key for it:
80
+
讓我們用一個 Symbol 的鍵值來處理:
80
81
81
82
```js run
82
-
let user = { //belongs to another code
83
+
let user = { //屬於另一份程式碼
83
84
name:"John"
84
85
};
85
86
86
87
let id =Symbol("id");
87
88
88
89
user[id] =1;
89
90
90
-
alert( user[id] ); //we can access the data using the symbol as the key
91
+
alert( user[id] ); //我們可以用 Symbol 當作鍵值來存取資料
91
92
```
92
93
93
-
What's the benefit of using `Symbol("id")` over a string `"id"`?
94
+
比起用字串 `"id"`,用 `Symbol("id")` 我們可以獲得什麼好處?
94
95
95
-
As`user`objects belongs to another code, and that code also works with them, we shouldn't just add any fields to it. That's unsafe. But a symbol cannot be accessed accidentally, the third-party code probably won't even see it, so it's probably all right to do.
96
+
當`user`物件屬於其他程式碼,且那些程式碼同樣會操作它時,我們不應該增加任何欄位到物件上。這樣不安全。但 Symbol 是沒辦法被意外存取的,第三方的程式碼甚至可能不會看到它,所以這麼做沒問題。
96
97
97
-
Also, imagine that another script wants to have its own identifier inside `user`, for its own purposes. That may be another JavaScript library, so that the scripts are completely unaware of each other.
Then that script can create its own `Symbol("id")`, like this:
100
+
然後該腳本可以創建自己的 `Symbol("id")`,像這樣:
100
101
101
102
```js
102
103
// ...
@@ -105,45 +106,46 @@ let id = Symbol("id");
105
106
user[id] ="Their id value";
106
107
```
107
108
108
-
There will be no conflict between our and their identifiers, because symbols are always different, even if they have the same name.
109
+
這不會造成我們與其他腳本之間的識別符有任何衝突,因為 Symbol 永遠是不同的,即使他們有相同的名稱。
109
110
110
-
...But if we used a string `"id"`instead of a symbol for the same purpose, then there *would* be a conflict:
111
+
...但如果我們用字串 `"id"`而非 Symbol,那麼 *就會* 發生衝突:
111
112
112
113
```js run
113
114
let user = { name:"John" };
114
115
115
-
//Our script uses "id" property
116
+
//我們的腳本使用 "id" 屬性
116
117
user.id="Our id value";
117
118
118
-
// ...Another script also wants "id" for its purposes...
119
+
// ...另一個腳本也想使用 "id"...
119
120
120
121
user.id="Their id value"
121
-
//Boom! overwritten by another script!
122
+
//碰!被另一個腳本給覆寫了!
122
123
```
123
124
124
-
### Symbols in a literal
125
+
### 字面值中的符號
125
126
126
-
If we want to use a symbol in an object literal `{...}`, we need square brackets around it.
127
+
如果我們想要在物件字面值 `{...}` 中使用 Symbol,我們需要方括號包圍它。
127
128
128
-
Like this:
129
+
像這樣:
129
130
130
131
```js
131
132
let id =Symbol("id");
132
133
133
134
let user = {
134
135
name:"John",
135
136
*!*
136
-
[id]:123//not "id: 123"
137
+
[id]:123//不是 "id: 123"
137
138
*/!*
138
139
};
139
140
```
140
-
That's because we need the value from the variable `id` as the key, not the string "id".
141
141
142
-
### Symbols are skipped by for..in
142
+
這是因為我們需要變數 `id` 的值當作鍵值,而非字串 "id"。
143
+
144
+
### 符號在 for..in 中被跳過
143
145
144
-
Symbolic properties do not participate in `for..in`loop.
146
+
Symbol 類型的屬性並不參與 `for..in`迴圈。
145
147
146
-
For instance:
148
+
例如:
147
149
148
150
```js run
149
151
let id =Symbol("id");
@@ -154,16 +156,17 @@ let user = {
154
156
};
155
157
156
158
*!*
157
-
for (let key in user) alert(key); // name, age (no symbols)
159
+
for (let key in user) alert(key); // name, age(沒有 Symbol)
158
160
*/!*
159
161
160
-
//the direct access by the symbol works
162
+
//直接存取 Symbol 是沒問題的
161
163
alert( "Direct: "+ user[id] );
162
164
```
163
165
164
-
`Object.keys(user)` also ignores them. That's a part of the general "hiding symbolic properties" principle. If another script or a library loops over our object, it won't unexpectedly access a symbolic property.
166
+
`Object.keys(user)` 也會忽略它們。那是一般 Symbol 中 "隱藏屬性" 原則的一部分。如果另一個腳本或是一個套件在我們的物件上循環,它不會不小心存取到 Symbol 類型的屬性。
167
+
168
+
相反地,[Object.assign](mdn:js/Object/assign) 同時複製字串與 Symbol 屬性:
165
169
166
-
In contrast, [Object.assign](mdn:js/Object/assign) copies both string and symbol properties:
167
170
168
171
```js run
169
172
let id =Symbol("id");
@@ -176,118 +179,118 @@ let clone = Object.assign({}, user);
176
179
alert( clone[id] ); // 123
177
180
```
178
181
179
-
There's no paradox here. That's by design. The idea is that when we clone an object or merge objects, we usually want *all* properties to be copied (including symbols like `id`).
182
+
這裡沒有矛盾,就是這樣設計的。想法是當我們複製一個物件,或是合併多個物件時,我們通常想要 *所有* 屬性都被複製(包含像 `id` 這樣的 Symbol )。
180
183
181
-
````smart header="Property keys of other types are coerced to strings"
182
-
We can only use strings or symbols as keys in objects. Other types are converted to strings.
184
+
````smart header="其他類別的屬性鍵值會被強行轉換成字串"
185
+
在物件中,我們只能使用字串或 Symbol 當作鍵值。其他類型都會被轉成字串。
183
186
184
-
For instance, a number `0` becomes a string `"0"` when used as a property key:
187
+
舉例來說,當一個數值 `0` 被用來當作屬性健值時,它會變成一個字串 `"0"`:
185
188
186
189
```js run
187
190
let obj = {
188
-
0: "test" // same as "0": "test"
191
+
0: "test" // 跟 "0": "test" 一樣
189
192
};
190
193
191
-
// both alerts access the same property (the number 0 is converted to string "0")
194
+
// 兩個 alerts 都存取到同樣的屬性(數值 0 被轉換成字串 "0")
192
195
alert( obj["0"] ); // test
193
-
alert( obj[0] ); // test (same property)
196
+
alert( obj[0] ); // test (同樣的屬性)
194
197
```
195
198
````
196
199
197
-
## Global symbols
200
+
## 全局符號
198
201
199
-
As we've seen, usually all symbols are different, even if they have the same name. But sometimes we want same-named symbols to be same entities. For instance, different parts of our application want to access symbol `"id"`meaning exactly the same property.
202
+
正如我們所見,通常所有的 Symbol 都是不同的,即使它們擁有相同的名稱。但是有時候我們想要擁有相同名稱的 Symbol 被當作相同的物體。例如,我們應用程式中的不同部分想用Symbol `"id"`存取到完全相同的屬性。
200
203
201
-
To achieve that, there exists a *global symbol registry*. We can create symbols in it and access them later, and it guarantees that repeated accesses by the same name return exactly the same symbol.
204
+
為此,存在一個 *全域 Symbol 註冊表(global symbol registry)*。我們可以在其中創建 Symbol 並在稍後存取它們,而這確保我們每次存取相同名稱都會回傳相同的 Symbol。。
202
205
203
-
In order to read (create if absent) a symbol from the registry, use `Symbol.for(key)`.
206
+
為了從註冊表中讀取(如果不存在就創建)Symbol,請使用 `Symbol.for(key)`。
204
207
205
-
That call checks the global registry, and if there's a symbol described as `key`, then returns it, otherwise creates a new symbol `Symbol(key)`and stores it in the registry by the given `key`.
208
+
該呼叫會檢查全域註冊表,若有個描述為 `key` 的 Symbol 存在,則回傳該 Symbol,否則就以 `Symbol(key)`創建新的 Symbol,並將其以 `key` 儲存到註冊表中。
206
209
207
-
For instance:
210
+
舉個例子:
208
211
209
212
```js run
210
-
//read from the global registry
211
-
let id =Symbol.for("id"); //if the symbol did not exist, it is created
213
+
//從全域註冊表中讀取
214
+
let id =Symbol.for("id"); //如果該 Symbol 不存在,就創建它
212
215
213
-
//read it again (maybe from another part of the code)
216
+
//再次讀取它(或許從程式碼的另一個部分)
214
217
let idAgain =Symbol.for("id");
215
218
216
-
//the same symbol
219
+
//同樣的 Symbol
217
220
alert( id === idAgain ); // true
218
221
```
219
222
220
-
Symbols inside the registry are called *global symbols*. If we want an application-wide symbol, accessible everywhere in the code -- that's what they are for.
In some programming languages, like Ruby, there's a single symbol per name.
225
+
```smart header="這聽起來像是 Ruby"
226
+
在某些程式語言中,像是 Ruby,每個名稱都只有單一個 symbol。
224
227
225
-
In JavaScript, as we can see, that's right for global symbols.
228
+
在 JavaScript 中,如我們所見,只有全域 Symbol 才是如此。
226
229
```
227
230
228
231
### Symbol.keyFor
229
232
230
-
For global symbols, not only `Symbol.for(key)`returns a symbol by name, but there's a reverse call: `Symbol.keyFor(sym)`, that does the reverse: returns a name by a global symbol.
233
+
對於全域 Symbol,不止有 `Symbol.for(key)`可以根據某個名稱回傳其 Symbol,還有個反向呼叫,`Symbol.keyFor(sym)` 做反過來的事:根據一個全域 Symbol 回傳其名稱。
231
234
232
-
For instance:
235
+
例如:
233
236
234
237
```js run
235
-
//get symbol by name
238
+
//根據名稱取得 Symbol
236
239
let sym =Symbol.for("name");
237
240
let sym2 =Symbol.for("id");
238
241
239
-
//get name by symbol
242
+
//根據 Symbol 取得名稱
240
243
alert( Symbol.keyFor(sym) ); // name
241
244
alert( Symbol.keyFor(sym2) ); // id
242
245
```
243
246
244
-
The `Symbol.keyFor`internally uses the global symbol registry to look up the key for the symbol. So it doesn't work for non-global symbols. If the symbol is not global, it won't be able to find it and return `undefined`.
247
+
`Symbol.keyFor`在內部使用全域 Symbol 註冊表來查詢 Symbol 的鍵值。所以它並不適用於非全域的 Symbol。如果某個 Symbol 不是全域的,此方法將無法找到它,並會回傳 `undefined`。
245
248
246
-
That said, any symbols have `description`property.
249
+
是說,任何 Symbol 都有 `description`屬性。
247
250
248
-
For instance:
251
+
例如:
249
252
250
253
```js run
251
254
let globalSymbol =Symbol.for("name");
252
255
let localSymbol =Symbol("name");
253
256
254
-
alert( Symbol.keyFor(globalSymbol) ); // name, global symbol
255
-
alert( Symbol.keyFor(localSymbol) ); // undefined, not global
257
+
alert( Symbol.keyFor(globalSymbol) ); // name, 全局 symbol
Other symbols will also become familiar when we study the corresponding language features.
277
+
當我們研讀相應的語言特性時,也將會更熟悉其他 Symbol。
275
278
276
-
## Summary
279
+
## 總結
277
280
278
-
`Symbol`is a primitive type for unique identifiers.
281
+
`Symbol`是用於標示唯一識別符號的原生類別。
279
282
280
-
Symbols are created with `Symbol()`call with an optional description (name).
283
+
Symbol 使用 `Symbol()`與一個可選的敘述(名稱)作為參數來創建。
281
284
282
-
Symbols are always different values, even if they have the same name. If we want same-named symbols to be equal, then we should use the global registry: `Symbol.for(key)`returns (creates if needed) a global symbol with `key`as the name. Multiple calls of `Symbol.for`with the same `key` return exactly the same symbol.
285
+
Symbol 永遠是不同的值,即使它們擁有相同的名稱。如果我們想要同名的 Symbol 也相等,那我們應該使用全域註冊表:`Symbol.for(key)`回傳(如果需要的話創建)一個以 `key` 作為名稱的全域 Symbol。以相同的 `key`用 `Symbol.for`多次進行呼叫,都會回傳完全相同的 Symbol。
283
286
284
-
Symbols have two main use cases:
287
+
Symbol 有兩個主要使用場景:
285
288
286
-
1. "Hidden" object properties.
287
-
If we want to add a property into an object that "belongs" to another script or a library, we can create a symbol and use it as a property key. A symbolic property does not appear in `for..in`, so it won't be accidentally processed together with other properties. Also it won't be accessed directly, because another script does not have our symbol. So the property will be protected from accidental use or overwrite.
289
+
1. "隱藏" 物件屬性。
290
+
如果我們想要增加一個屬性到 "屬於" 其他腳本或是套件的物件之中,我們可以創建一個 Symbol 並用它當作屬性的鍵值。Symbol 屬性不會出現在 `for..in` 中,所以他不會不小心被其他屬性一起處理。此外它也不能被直接存取,因為其他腳本不擁有我們的 Symbol。所以該屬性將會被保護,以防意外被存取或覆寫。
288
291
289
-
So we can "covertly" hide something into objects that we need, but others should not see, using symbolic properties.
292
+
所以我們可以使用 Symbol 屬性, "秘密地" 將一些我們需要,但其他人不需要的東西藏進物件中。
290
293
291
-
2.There are many system symbols used by JavaScript which are accessible as `Symbol.*`. We can use them to alter some built-in behaviors. For instance, later in the tutorial we'll use `Symbol.iterator` for [iterables](info:iterable), `Symbol.toPrimitive` to setup [object-to-primitive conversion](info:object-toprimitive)and so on.
Technically, symbols are not 100% hidden. There is a built-in method [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols)that allows us to get all symbols. Also there is a method named [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys)that returns *all* keys of an object including symbolic ones. So they are not really hidden. But most libraries, built-in functions and syntax constructs don't use these methods.
0 commit comments