Lesson 34: Mastering JavaScript Symbols with challenges!

๐งฉ What is a Symbol?
A Symbol in JavaScript is a primitive data type introduced in ES6. It represents a unique, immutable identifier.
const sym1 = Symbol();
const sym2 = Symbol("id");
console.log(sym1 === sym2); // false
๐ ๏ธ Why use Symbols?
Symbols are mainly used as non-colliding property keys on objects. They:
Avoid name clashes in shared objects.
Stay hidden from
for...in
,Object.keys()
, etc.Enable safe extension of objects across modules or libraries.
๐ Symbol in Objects
const ID = Symbol("userID");
const user = {
name: "Alice",
[ID]: 1234
};
console.log(user[ID]); // 1234
console.log(Object.keys(user)); // ["name"]
๐ Global Symbols (Shared Keys)
const globalSym1 = Symbol.for("shared");
const globalSym2 = Symbol.for("shared");
console.log(globalSym1 === globalSym2); // true
๐ก Description vs Global Key
const localSym = Symbol("id");
console.log(localSym.description); // "id"
console.log(Symbol.keyFor(localSym)); // undefined (not global)
๐น 2. Fill Any Gaps
๐ณ๏ธ Missing Subtopics & Deep Mechanics
Well-known Symbols: Built-in hooks like
Symbol.iterator
,Symbol.toPrimitive
, etc.Object.getOwnPropertySymbols(obj) exposes hidden symbol keys.
Reflect.ownKeys(obj) lists all keys (including symbols and strings).
Symbol.isConcatSpreadable modifies array behavior with
concat()
.
โ Developer Confusions
Symbols are not private: Theyโre hidden, but retrievable via reflection.
Symbol("id") !== Symbol("id") even with the same description.
Cannot stringify directly:
String(symbol)
orsymbol.description
must be used.
console.log(String(Symbol("test"))); // "Symbol(test)"
console.log(Symbol("test") + ""); // TypeError
โ ๏ธ Caveats
Not enumerable: Symbol properties wonโt show up in many object utilities.
May break serialization:
JSON.stringify()
skips symbol-keyed properties.Global registry is app-wide โ avoid pollution/conflicts.
๐น 3. Challenge Me Deeply
๐ข Basic
Create a user object with a symbol key that stores the user's internal ID.
Demonstrate that two symbols with the same description are not equal.
Retrieve symbol-keyed properties using
Object.getOwnPropertySymbols
.
๐ก Intermediate
Implement a class that uses a symbol to hide a private method.
Use
Symbol.for()
to share a symbol between two files/modules.Clone an object with both string and symbol properties using
Object.assign
.
๐ด Advanced
Create a custom object with a
Symbol.iterator
that returns Fibonacci numbers.Override the
Symbol.toPrimitive
to return custom conversion logic.Use
Symbol.isConcatSpreadable
to make a non-array object work like an array.
๐ง Brain Twister
- Explain why this logs
"Symbol(Symbol.iterator)"
but not"Symbol.iterator"
:
console.log(String(Symbol.iterator));
Then modify the code so that you get "Symbol.iterator"
as a string.
๐น 4. Interview-Ready Questions
๐ Conceptual
Whatโs the difference between
Symbol("id")
andSymbol.for("id")
?Why are symbols not enumerable in
for...in
loops?
๐ Scenario
- You're integrating with a third-party library. How can you store custom metadata in their objects without risking property name conflicts?
๐ Debugging
- Why would
JSON.stringify(obj)
return{}
when your object clearly has properties?
โ Best Practices
Use
Symbol.for
for cross-file or module-level constants.Prefer local
Symbol()
for unique keys to avoid registry pollution.Avoid symbol keys in data that needs serialization or transport.
๐น 5. Real-World Usage
๐ In Frameworks/Libraries
Redux: Uses symbols for action types or metadata internally.
Babel/TypeScript: Use well-known symbols like
Symbol.iterator
.Node.js:
util.inspect.custom
is a well-known symbol used to customize console output.
๐งช Example: Iterable Custom Object
const range = {
from: 1,
to: 5,
[Symbol.iterator]() {
let current = this.from;
let last = this.to;
return {
next() {
return current <= last ? { done: false, value: current++ } : { done: true };
}
};
}
};
for (let num of range) {
console.log(num); // 1, 2, 3, 4, 5
}
๐น 6. Remember Like a Pro
๐ง Mnemonics
S.H.I.E.L.D. for Symbol use cases:
Safe keying
Hidden from loops
Isolated identity
Entry to global registry
Low-level JS meta programming
Debuggable description
๐งพ Cheatsheet
Feature | Symbol() | Symbol.for() |
Unique per call? | โ | โ (Shared) |
Has global registry? | โ | โ |
Use case | Hidden local keys | Shared app-wide keys |
String conversion | โ (toString() needed) | โ |
๐น 7. Apply It in a Fun Way
๐ฎ Mini Project: Symbol-Powered Access Logger
Goal: Log internal API call counts without exposing them to external code.
๐ Steps:
Create a
Symbol('internalCounter')
to hide usage tracking.Build a
trackableFetch()
wrapper that increments the counter.Log results only when a secret function is called.
Prevent external code from accessing the counter via
for...in
.
const usageCounter = Symbol("usage");
function createTracker() {
return {
[usageCounter]: 0,
fetch(url) {
this[usageCounter]++;
return fetch(url);
},
showUsage() {
console.log(`Used ${this[usageCounter]} times`);
}
};
}
const api = createTracker();
api.fetch("https://api.com/data");
api.showUsage(); // Used 1 times
โ Extensions:
Add
Symbol.iterator
to iterate over API call timestamps.Add a
Symbol.toPrimitive
for custom alert message.
๐ง (Optional) Extra Value
๐ Open-Source Projects
- MobX, React DevTools, Apollo Client use symbols to hide metadata.
โ ๏ธ Common Mistakes
Using symbols as regular values (e.g., comparing across modules incorrectly).
Forgetting they canโt be serialized (breaks APIs using
JSON.stringify
).
๐ Performance Tip
Symbol lookup is as fast as string properties but more secure for metadata.
Use
Object.getOwnPropertySymbols()
sparingly โ it's slower than normal key access.
Subscribe to my newsletter
Read articles from manoj ymk directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
