Step 1 of 7 · Reading · ~3 min
Learn
The Runtime
Symbols and Well-Known Symbols
A symbol is a primitive value that is guaranteed unique. That makes it a property key nobody else can collide with, and it is also how the language lets your objects plug into built-in behaviour such as for...of, +, instanceof and String(x). By the end you can make a class control how it converts to numbers and strings.
Unique values, used as keys
Every call to Symbol() makes a new value; the optional description is only a label for debugging.
That hiding is a convenience, not privacy: anyone with the object can list its symbols. Use #private fields when you need real encapsulation.
Symbol.for('app.id') instead asks a process-wide registry: the same string always returns the same symbol, in every module. Symbol.keyFor(sym) reads the string back, and gives undefined for a symbol made with plain Symbol().
A symbol never converts to a string implicitly: Symbol() + '' throws a TypeError. Use String(sym) or .description.
Well-known symbols: hooks into the language
The Symbol constructor carries a set of ready-made symbols. Defining a method under one of them changes how a built-in operation treats your object:
| Symbol | Operation it customises |
|---|---|
Symbol.iterator | for...of, spread, destructuring (you built this in Intermediate) |
Symbol.asyncIterator | for await...of |
Symbol.hasInstance | x instanceof C (a static method on C) |
Symbol.toStringTag | the tag in Object.prototype.toString.call(x) |
Symbol.toPrimitive | converting the object to a number or string |
Symbol.toPrimitive and its three hints
When an object meets an operator that needs a primitive, the engine calls its [Symbol.toPrimitive](hint) method with a hint saying what the operation wants. Without one, it falls back to valueOf() and toString(), which is where [object Object] and NaN come from.
| The hint is... | when the object is used in |
|---|---|
'number' | +x, x - y, x * y, x < y, x > y, Math.max(x) |
'string' | `${x}`, String(x), obj[x] as a property key |
'default' | x + y, x + '', x == 'text' |
The 'default' hint is the one that surprises people. If your method returns the number for it, t + '' becomes '125' and == against text stops working. Decide which form default should take and return that; the built-in Date returns its string form there.
Common mistakes
- Writing
[id]: valueasid: value. Without the brackets the key is the string'id'. - Assuming
Object.keysorJSON.stringifywill show a symbol-keyed property. Neither does. - Handling only
'number'and'string'intoPrimitive.+and==pass'default', so it needs a deliberate answer.
Your exercise
You will give a Version class its own conversions, so that sorting, comparison and printing all work on the objects directly. The usual slip is the 'default' hint: the starter prints the newest version through +, and a sort by a - b needs numbers while printing needs text.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…