Learnitweb

Symbol in JavaScript

1. Introduction

In this tutorial, we’ll discuss in detail about a primitive type introduced in ECMAScript 6 – Symbol. The JavaScript already had five primitive types: string, number, boolean, null, and undefined before Symbol was introduced. In this tutorial, we’ll discuss Symbol and how to create a Symbol. We’ll also discuss about Symbol properties. But before we discuss all this, we should first discuss why do we need a Symbol.

Symbol is a built-in object which returns a guaranteed unique value – a symbol primitive.

2. Need for Symbol

Suppose you are having a collection of User objects which you get from some other source. A User object may look like this:

let user = {
  id: 1234,
  name: "James",
  city: "London",
  age: 19,
}

Suppose you want to attach a unique identifier to this user object to distinguish the objects within your system. You can think of doing something like this:

user.id = 123458676; // 123458676 is unique id value

But the problem here is, user object already has an id. Since we don’t control the user object as it is coming from a different object, we have to think of using a different property name. There could be different real world situations where we have requirement to add a unique property to the object but it may have a conflicting name.

To avoid such situation, we can use Symbol. We can create a Symbol which guarantees a unique value and use it like this:

let user = {
  id: 1234,
  name: "James",
  city: "London",
  age: 19,
};

const idSymb = Symbol("id");
user[idSymb] = 123458676;

console.log(user);

Output

{
  id: 1234,
  name: 'James',
  city: 'London',
  age: 19,
  [Symbol(id)]: 123458676
}

Symbols are often used to add unique property keys to an object that won’t collide with keys. These unique property keys are hidden and can’t be access by usual means.

3. Create a Symbol

To create a a new primitive Symbol, use Symbol(). You can optionally provide a string to the Symbol() which acts as a descriptor to the Symbol. A symbol’s description is stored internally in the [[Description]] property. This property is read whenever the symbol’s toString() method is called either explicitly or implicitly. Otherwise it is not possible to access [[Description]] directly from code.

let symbl1 = Symbol();
let symbl2 = Symbol("test");
let symbl3 = Symbol("test");

All Symbol values symbl1, symbl2 and symbl3 are unique.

console.log(symbl1 === symbl2);  // false
console.log(symbl1 === symbl3);  // false
console.log(symbl2 === symbl3);  // false

Providing an optional string descriptor to Symbol does not create string to Symbol. Using Symbol() always provides a new Symbol value.

4. Can’t use new with Symbol

You can not not use new with Symbol. Using new with Symbol will throw a TypeError:

let symbl1 = new Symbol(); // TypeError: Symbol is not a constructor

Since you can’t use new with Symbol, you can’t create wrapper object with Symbol like you can do with other primitives, for example new String, new Boolean and new Number.

If you want to create wrapper for Symbol, use Object() function.

let sym = Symbol('foo') // create Symbol
let symObj = Object(sym); // Symbol wrapper object

5. Adding Symbol to object

Symbol is used to add unique property keys in object.

let symb = Symbol();

//wrong way to create Symbol property key
let user = {
  id: 1234,
  name: "James",
  city: "London",
  age: 19,
  symb: 12345,
};

console.log(user);

Output

The correct way to do it is like this:

let symb = Symbol();
let user = {
  id: 1234,
  name: "James",
  city: "London",
  age: 19,
  [symb]: 12345,
};

console.log(user);

Output

{ id: 1234, name: 'James', city: 'London', age: 19, [Symbol()]: 12345 }

6. Identifying Symbols (typeof)

You can use the typeof operator to determine whether a variable contains a symbol.

let symb = Symbol();
console.log(typeof symb); //symbol

7. Sharing Symbols

You might need different parts of your code to share same symbols. Suppose there are two different types of objects in your application which should use the same unique identifier. If you manage it yourself, it could be difficult and error prone. ECMAScript 6 provides a global symbol registry that you can access.

7.1 Symbol.for()

When you want to create a symbol which you want to be shared, use Symbol.for() instead of Symbol(). The Symbol.for() method accepts a single string parameter which acts as an identifier for the symbol.

let symb1 = Symbol("id");
let symb2 = Symbol.for("id");
let symb3 = Symbol.for("id");

console.log(symb1); // Symbol(id)
console.log(symb2); // Symbol(id)

console.log(symb1 === symb2); // false
console.log(symb2 === symb3); // true

The Symbol.for() method first searches the global symbol registry to see whether a symbol with the key id exists. If found, the method returns the existing symbol. If not found, a new symbol is created and registered to the global symbol registry using the specified key. The new symbol is then returned. Subsequent calls to Symbol.for() using the same key will return the same
symbol.

7.2 Symbol.keyFor()

Using Symbol.keyFor() you can retrieve the key associated with a symbol in the global symbol registry.

let id = Symbol.for("id");
console.log(Symbol.keyFor(id)); // "id"

let id2 = Symbol("id");
console.log(Symbol.keyFor(id2)); // "undefined" since id2 is not in global symbol registry

8. Symbol Coercion

Symbols cannot be coerced into strings or numbers to prevent them from being accidentally used as properties.

let id = Symbol.for("id");
desc = id + ""; // TypeError: Cannot convert a Symbol value to a string

Similarly, you cannot coerce a symbol to a number. All mathematical operators cause an error when they’re applied to a symbol. For example:

let sym = Symbol.for("id");
sum = sym / 1; // TypeError: Cannot convert a Symbol value to a number

9. Retrieving Symbol properties

Neither of the two functions Object.keys() and Object.getOwnPropertyNames() methods can be used to retrieve property symbols from the object. You can use the Object.getOwnPropertySymbols() method to retrieve property symbols from an object.

let user = {
  id: 1234,
  name: "James",
  city: "London",
  age: 19,
};

const idSymb = Symbol("id");
user[idSymb] = 123458676;

console.log(Object.getOwnPropertyNames(user)); // [ 'id', 'name', 'city', 'age' ]
console.log(Object.getOwnPropertySymbols(user)); // [ Symbol(id) ]

Object.getOwnPropertySymbols() returns an array containing symbols.

10. Symbols and for…in iteration

Symbols are not enumerable in for…in iterations.

let user = {
  id: 1234,
  name: "James",
  city: "London",
  age: 19,
};

const idSymb = Symbol("id");
user[idSymb] = 123458676;

for (key in user) {
  console.log(key);
}

Output

id
name
city
age

11. Symbols and JSON.stringify()

Symbol properties will be completely ignored when using JSON.stringify():

let user = {
  id: 1234,
  name: "James",
  city: "London",
  age: 19,
};

const idSymb = Symbol("id");
user[idSymb] = 123458676;

console.log(JSON.stringify(user));

Output

{"id":1234,"name":"James","city":"London","age":19}

12. Symbol wrapper objects as property keys

When a Symbol wrapper object is used as a property key, this object will be coerced to its wrapped Symbol:

let user = {
  id: 1234,
  name: "James",
  city: "London",
  age: 19,
};

const idSymb = Symbol("id");
user[Object(idSymb)] = 123458676; // using Symbol wrapper

console.log(user);

Output

{
  id: 1234,
  name: 'James',
  city: 'London',
  age: 19,
  [Symbol(id)]: 123458676
}

13. Conclusion

In this tutorial, we discussed Symbol, its need, creation and usage. This tutorial provides an overview of Symbol and is not complete. There are other properties and methods associated with Symbol. You are requested to read official documentation for the same. This tutorial is enough to get started with Symbol. We hope this tutorial was informative. Happy learning!