Lesson 34: Mastering JavaScript Symbols with challenges!

manoj ymkmanoj ymk
5 min read

๐Ÿงฉ 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) or symbol.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

  1. Create a user object with a symbol key that stores the user's internal ID.

  2. Demonstrate that two symbols with the same description are not equal.

  3. Retrieve symbol-keyed properties using Object.getOwnPropertySymbols.

๐ŸŸก Intermediate

  1. Implement a class that uses a symbol to hide a private method.

  2. Use Symbol.for() to share a symbol between two files/modules.

  3. Clone an object with both string and symbol properties using Object.assign.

๐Ÿ”ด Advanced

  1. Create a custom object with a Symbol.iterator that returns Fibonacci numbers.

  2. Override the Symbol.toPrimitive to return custom conversion logic.

  3. Use Symbol.isConcatSpreadable to make a non-array object work like an array.

๐Ÿง  Brain Twister

  1. 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") and Symbol.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

FeatureSymbol()Symbol.for()
Unique per call?โœ…โŒ (Shared)
Has global registry?โŒโœ…
Use caseHidden local keysShared 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:

  1. Create a Symbol('internalCounter') to hide usage tracking.

  2. Build a trackableFetch() wrapper that increments the counter.

  3. Log results only when a secret function is called.

  4. 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.

0
Subscribe to my newsletter

Read articles from manoj ymk directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

manoj ymk
manoj ymk