Skip to content

Commit faa253c

Browse files
committed
types-grammar, ch1: finished chapter 1 draft
1 parent eb0ad3b commit faa253c

File tree

3 files changed

+110
-2
lines changed

3 files changed

+110
-2
lines changed

types-grammar/ch1.md

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -948,13 +948,114 @@ secret = Symbol("my secret");
948948
| :--- |
949949
| Just as with `BigInt(..)`, the `Symbol(..)` function must be called without the `new` keyword. |
950950
951-
The `"my secret"` string passed into the `Symbol` is *not* the symbol value itself, even though it seems that way. It's an optional descriptive label, used only for debugging purposes for the benefit of the developer.
951+
The `"my secret"` string passed into the `Symbol(..)` function call is *not* the symbol value itself, even though it seems that way. It's merely an optional descriptive label, used only for debugging purposes for the benefit of the developer.
952952
953953
The underlying value returned from `Symbol(..)` is a special kind of value that resists the program/developer inspecting anything about its underlying representation. That's what I mean by "opaque".
954954
955+
| NOTE: |
956+
| :--- |
957+
| You could think of symbols as if they are monotonically incrementing integer numbers -- indeed, that's similar to how at least some JS engines implement them. But the JS engine will never expose any representation of a symbol's underlying value in any way that you or the program can see. |
958+
955959
Symbols are guaranteed by the JS engine to be unique (only within the program itself), and are unguessable. In other words, a duplicate symbol value can never be created in a program.
956960
957-
// TODO
961+
You might be wondering at this point what symbols are used for?
962+
963+
One typical usage is as "special" values that the developer distinguishes from any other values that could accidentally collide. For example:
964+
965+
```js
966+
EMPTY = Symbol("not set yet");
967+
myNickname = EMPTY;
968+
969+
// later:
970+
971+
if (myNickname == EMPTY) {
972+
// ..
973+
}
974+
```
975+
976+
Here, I've defined a special `EMPTY` value and initialized `myNickname` to it. Later, I check to see if it's still that special value, and then perform some action if so. I might not want to have used `null` or `undefined` for such purposes, as another developer might be able to pass in one of those common built-in values. `EMPTY` by contrast here is a unique, unguessable value that only I've defined and have control over and access to.
977+
978+
Perhaps even more commonly, symbols are often used as special (meta-) properties on objects:
979+
980+
```js
981+
myInfo = {
982+
name: "Kyle Simpson",
983+
nickname: "getify",
984+
age: 42
985+
};
986+
987+
// later:
988+
PRIVATE_ID = Symbol("private unique ID, don't touch!");
989+
990+
myInfo[PRIVATE_ID] = generateID();
991+
```
992+
993+
It's important to note that symbol properties are still publicly visible on any object; they're not *actually* private. But they're treated as special and set-apart from the normal collection of object properties. It's similar to if I had done instead:
994+
995+
```js
996+
Object.defineProperty(myInfo,"__private_id_dont_touch",{
997+
value: generateID(),
998+
enumerable: false,
999+
});
1000+
```
1001+
1002+
By convention only, most developers know that if a property name is prefixed with `_` (or even moreso, `__`!), that means it's "pseudo-private" and to leave it alone unless they're really supposed to access it.
1003+
1004+
Symbols basically serve the same use-case, but a bit more ergonomically than the prefixing approach.
1005+
1006+
### Well-Known Symbols (WKS)
1007+
1008+
JS pre-defines a set of symbols, referred to as *well-known symbols* (WKS), that represent certain special meta-programming hooks on objects. These symbols are stored as static properties on the `Symbol` function object. For example:
1009+
1010+
```js
1011+
myInfo = {
1012+
// ..
1013+
};
1014+
1015+
String(myInfo); // [object Object]
1016+
1017+
myInfo[Symbol.toStringTag] = "my-info";
1018+
String(myInfo); // [object my-info]
1019+
```
1020+
1021+
`Symbol.toStringTag` is a well-known symbol for accessing and overriding the default string representation of a plain object (`"[object Object]"`), replacing the `"Object"` part with a different value (e.g., `"my-info"`).
1022+
1023+
See the "Objects & Classes" book of this series for more information about Well-Known Symbols and metaprogramming.
1024+
1025+
### Global Symbol Registry
1026+
1027+
Often, you want to keep symbol values private, such as inside a module scope. But occasionally, you want to expose them so they're accessible globally throughout all the files in a JS program.
1028+
1029+
Instead of just attaching them as global variables (i.e., properties on the `globalThis` object), JS provides an alternate *global namespace* to register symbols in:
1030+
1031+
```js
1032+
// retrieve if already registered,
1033+
// otherwise register
1034+
PRIVATE_ID = Symbol.for("private-id");
1035+
1036+
// elsewhere:
1037+
1038+
privateIDKey = Symbol.keyFor(PRIVATE_ID);
1039+
privateIDKey; // "private-id"
1040+
1041+
// elsewhere:
1042+
1043+
// retrieve symbol from registry undeer
1044+
// specified key
1045+
privateIDSymbol = Symbol.for(privateIDKey);
1046+
```
1047+
1048+
The value passed to `Symbol.for(..)` is *not* the same as passed to `Symbol(..)`. `Symbol.for(..)` expects a unique *key* for the symbol to be registered under in the global registry, whereas `Symbol(..)` optionally accepts a descriptive label (not necessarily unique).
1049+
1050+
If the registry doesn't have a symbol under that specified *key*, a new symbol (with no descriptive label) is created and automatically registered there. Otherwise, `Symbol.for(..)` returns whatever previously registered symbol is under that *key*.
1051+
1052+
Going in the opposite direction, if you have the symbol value itself, and want to retrieve the *key* it's registered under, `Symbol.keyFor(..)` takes the symbol itself as input, and returns the *key* (if any). That's useful in case it's more convenient to pass around the *key* string value than the symbol itself.
1053+
1054+
## Primitives Are Built-In Types
1055+
1056+
We've now dug deeply into the seven primitive (non-object) value types that JS provides automatically built-in.
1057+
1058+
Before we move on to discussing JS's built-in object value type, we want to take a closer look at the kinds of behaviors we can expect from JS values. We'll do so in-depth, in the next chapter.
9581059
9591060
[^UTFUCS]: "JavaScript’s internal character encoding: UCS-2 or UTF-16?"; Mathias Bynens; January 20 2012; https://mathiasbynens.be/notes/javascript-encoding ; Accessed July 2022
9601061

types-grammar/ch2.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# You Don't Know JS Yet: Types & Grammar - 2nd Edition
22
# Chapter 2: Value Behavior
33

4+
So far, we've explored seven built-in primitive value types in JS: `null`, `undefined`, `boolean`, `string`, `number`, `bigint`, and `symbol`.
5+
6+
Chapter 1 was quite a lot to take in, much more involved than I bet most readers expected. If you're still catching your breath after reading all that, don't worry about taking a bit of a break before continuing on here!
7+
8+
Once you're clear headed and ready to move on, let's dig into certain behaviors implied by value types for all their respective values. We'll take a careful and closer look at all of these various behaviors.
9+
410
## Value Immutability
511

612
All primitive values are immutable, meaning nothing in a JS program can reach into the inside of the value and modify it in any way.

types-grammar/toc.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* Number Values
1717
* BigInteger Values
1818
* Symbol Values
19+
* Primitives Are Built-In Types
1920
* Chapter 2: Value Behavior
2021
* Value Immutability
2122
* Assignments Are Value Copies

0 commit comments

Comments
 (0)