From 2b49b43f790420441a01c8d74ff16ffe630b09d3 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Tue, 4 Sep 2018 12:10:01 +0200 Subject: [PATCH] doc: added symbols guidelines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/22684 Reviewed-By: Tobias Nießen Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater Reviewed-By: Luigi Pinca Reviewed-By: Vse Mozhet Byt Reviewed-By: Daniel Bevenius Reviewed-By: Anna Henningsen Reviewed-By: Anatoli Papirovski --- doc/guides/using-symbols.md | 73 +++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 doc/guides/using-symbols.md diff --git a/doc/guides/using-symbols.md b/doc/guides/using-symbols.md new file mode 100644 index 00000000000000..1e79e1a4e4b11c --- /dev/null +++ b/doc/guides/using-symbols.md @@ -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.