Introduction

If you've ever worked with JavaScript, you've probably encountered data types like strings, numbers, objects, and arrays. But did you know there’s another, more mysterious primitive type called Symbols? They’re not as widely discussed as other data types, but mastering Symbols can give you new powers in JavaScript—especially when working on large codebases or creating libraries where you need to avoid name collisions.

In this article, we’ll dive deep into Symbols, understand their use cases, and explore how they differ from other data types. We’ll also touch on symbol properties, their unique behavior, and how they help with creating more flexible, robust JavaScript applications.

What Are Symbols?

A Symbol is a primitive data type introduced in ES6 (ECMAScript 2015). It is a unique, immutable value that is primarily used to create unique object keys. What makes Symbols stand out is that, even if two symbols have the same description, they are always unique.

Here's a simple example:


_10
javascript
_10
Copy code
_10
const symbol1 = Symbol('description');
_10
const symbol2 = Symbol('description');
_10
_10
console.log(symbol1 === symbol2); // false

Despite both symbol1 and symbol2 having the same description ('description'), they are completely different values. This uniqueness makes Symbols incredibly useful for defining keys in objects, especially when you want to avoid accidental key collisions.

Why Use Symbols?

Symbols offer two primary benefits:

  1. Uniqueness: As we’ve seen, no two symbols are the same, even if they share a description. This ensures that when you define properties on objects using Symbols, they won’t conflict with other properties.
  2. Non-enumerable: By default, Symbol properties are not included in normal object property enumerations (such as for...in loops or Object.keys()), providing a layer of protection for these properties.

Using Symbols as Object Keys

Let’s start by seeing how Symbols can be used as object keys. In JavaScript, most object keys are strings, but Symbols allow you to create object properties that are unique and hidden from loops like for...in.


_11
javascript
_11
Copy code
_11
const animal = {
_11
name: 'Dog'
_11
};
_11
_11
const species = Symbol('species');
_11
animal[species] = 'Canine';
_11
_11
console.log(animal.name); // 'Dog'
_11
console.log(animal[species]); // 'Canine'

In this example, the species Symbol is used as a key in the animal object. If you inspect the object, you'll see only the name property:


_10
javascript
_10
Copy code
_10
console.log(Object.keys(animal)); // ['name']

Notice that species does not show up because Symbols are non-enumerable by default. This means for...in, Object.keys(), and similar methods will not pick up Symbol properties unless explicitly asked for with methods like Object.getOwnPropertySymbols():


_10
javascript
_10
Copy code
_10
console.log(Object.getOwnPropertySymbols(animal)); // [ Symbol(species) ]

Global Symbols: Symbol.for() and Symbol.keyFor()

Symbols created with the Symbol() function are unique and cannot be referenced elsewhere in your code. However, there are times when you need to use the same Symbol across different parts of your codebase. That’s where global Symbols come into play.

JavaScript provides the Symbol.for() method to create and access global symbols. If a Symbol with a given key already exists, Symbol.for() returns that existing Symbol. Otherwise, it creates a new one.


_10
javascript
_10
Copy code
_10
const globalSymbol1 = Symbol.for('global');
_10
const globalSymbol2 = Symbol.for('global');
_10
_10
console.log(globalSymbol1 === globalSymbol2); // true

Here, globalSymbol1 and globalSymbol2 are the same Symbol because they both refer to the same global key ('global'). To retrieve the key of a global Symbol, you can use Symbol.keyFor():


_10
javascript
_10
Copy code
_10
console.log(Symbol.keyFor(globalSymbol1)); // 'global'

Built-In Well-Known Symbols

JavaScript includes several well-known Symbols that are used to customize how certain behaviors of objects work. These well-known Symbols are predefined by the language and can be used to modify object behaviors like iteration, string conversion, and more.

Some of the most common well-known Symbols include:

  • Symbol.iterator: Used to define the default iterator for an object, allowing you to customize how an object behaves in for...of loops.
  • Symbol.toStringTag: Controls the default string description of an object (as returned by Object.prototype.toString()).

Let’s look at an example of using Symbol.iterator:


_14
javascript
_14
Copy code
_14
const dog = {
_14
name: 'Rex',
_14
age: 5,
_14
[Symbol.iterator]: function* () {
_14
yield this.name;
_14
yield this.age;
_14
}
_14
};
_14
_14
for (const prop of dog) {
_14
console.log(prop); // 'Rex', 5
_14
}

In this case, by adding a custom iterator using Symbol.iterator, we can control how our dog object behaves in for...of loops. Without this symbol, dog would not be iterable.

Symbols in Real Projects

Now, let’s consider a practical use case for Symbols: a plugin system for a JavaScript library. Let’s say we are building a library where developers can attach plugins. We don’t want the plugin properties to interfere with core functionality, so we use Symbols to keep plugin-specific keys hidden and unique.


_20
javascript
_20
Copy code
_20
const PLUGIN_KEY = Symbol('plugin');
_20
_20
function attachPlugin(libObject, plugin) {
_20
libObject[PLUGIN_KEY] = plugin;
_20
}
_20
_20
function getPlugin(libObject) {
_20
return libObject[PLUGIN_KEY];
_20
}
_20
_20
// Example usage:
_20
const myLibrary = { name: 'AwesomeLib' };
_20
_20
const myPlugin = { name: 'SuperPlugin' };
_20
attachPlugin(myLibrary, myPlugin);
_20
_20
console.log(myLibrary.name); // 'AwesomeLib'
_20
console.log(getPlugin(myLibrary)); // { name: 'SuperPlugin' }

In this example, we use a Symbol to store the plugin in myLibrary, ensuring that it won’t accidentally conflict with any other properties in the library.

Conclusion

In this article, we’ve explored the mysterious and powerful Symbols in JavaScript. We’ve seen how they can help create unique, hidden object keys and avoid property collisions—perfect for scenarios where you need to manage complex systems or libraries. Additionally, we touched on global Symbols and well-known Symbols, showing their importance in customizing object behaviors.

To summarize:

  • Symbols are unique and immutable, perfect for creating object keys that won’t clash.
  • They are non-enumerable, keeping them hidden from common operations like for...in.
  • You can use global Symbols when you need to reference the same Symbol across different parts of your code.
  • Well-known Symbols allow you to customize object behaviors like iteration and string conversion.

With these tools at your disposal, you can create more robust, flexible JavaScript applications. Keep exploring Symbols, and you’ll find new ways to make your code more maintainable and less prone to conflicts.

Further Reading

Reinforce Your Learning

One of the best ways to reinforce what your learning is to test yourself to solidify the knowlege in your memory.
Complete this 2 question quiz to see how much you remember.

1) What is the primary benefit of using Symbols as object keys in JavaScript?

Thanks alot for your feedback!

The insights you share really help me with improving the quality of the content here.

If there's anything you would like to add, please send a message to:

[email protected]

Was this article this helpful?

D is for danny

About the author

Danny Engineering

A software engineer with a strong belief in human-centric design and driven by a deep empathy for users. Combining the latest technology with human values to build a better, more connected world.

Related Articles

Gobacktothetop

Made with 🐾 in 🏴󠁧󠁢󠁥󠁮󠁧󠁿

©2024 All rights reserved.