-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PR-URL: #22684 Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Vse Mozhet Byt <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Anatoli Papirovski <[email protected]>
- Loading branch information
Showing
1 changed file
with
73 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# Using global symbols | ||
|
||
ES6 introduced a new type: `Symbol`. This new type is _immutable_, and | ||
it is often used for metaprogramming purposes, as it can be used as | ||
property keys like string. There are two types of | ||
symbols, local and global. | ||
Symbol-keyed properties of an object are not included in the output of | ||
`JSON.stringify()`, but the `util.inspect()` function includes them by | ||
default. | ||
|
||
Learn more about symbols at | ||
https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol. | ||
|
||
## `Symbol(string)` | ||
|
||
Symbols created via `Symbol(string)` are local to the caller function. | ||
Note that `Symbol('hello') !== Symbol('hello')`. | ||
For this reason, we often use them to simulate private fields, like so: | ||
|
||
```js | ||
const kField = Symbol('kField'); | ||
|
||
console.log(kField === Symbol('kField')); // false | ||
|
||
class MyObject { | ||
constructor() { | ||
this[kField] = 'something'; | ||
} | ||
} | ||
|
||
module.exports.MyObject = MyObject; | ||
``` | ||
|
||
Note that Symbols are not _fully private_, as the data could be accessed | ||
anyway: | ||
|
||
```js | ||
for (const s of Object.getOwnPropertySymbols(obj)) { | ||
const desc = s.toString().replace(/Symbol\((.*)\)$/, '$1'); | ||
if (desc === 'kField') { | ||
console.log(obj[s]); // 'something' | ||
} | ||
} | ||
``` | ||
|
||
Local symbols make it harder for developers to monkey patch/access | ||
private fields, as they require more work than a property prefixed | ||
with an `_`. Monkey patching private API that were not designed to be | ||
monkey-patchable make maintaining and evolving Node.js harder, as private | ||
properties are not documented and can change within a patch release. | ||
Some extremely popular modules in the ecosystem monkey patch some | ||
internals, making it impossible for us to update and improve those | ||
areas without causing issues for a significant amount of users. | ||
|
||
## `Symbol.for` | ||
|
||
Symbols created with `Symbol.for(string)` are global and unique to the | ||
same V8 Isolate. On the first call to `Symbol.for(string)` a symbol is | ||
stored in a global registry and easily retrieved for every call of | ||
`Symbol.for(string)`. However, this might cause problems when two module | ||
authors use the same symbol | ||
for different reasons. | ||
|
||
```js | ||
const s = Symbol.for('hello'); | ||
console.log(s === Symbol.for('hello')); | ||
``` | ||
|
||
In the Node.js runtime we prefix all our global symbols with `nodejs.`, | ||
e.g. `Symbol.for('nodejs.hello')`. | ||
|
||
Global symbols should be preferred when a developer-facing interface is | ||
needed to allow behavior customization, i.e., metaprogramming. |