It's a primitive data of Javascript, along with String, Number (Integers, Floats, Infinity, NaN), Boolean, null, and undefined. And it represents a "unique" Identifier. Introduced in ECMAScript 2015, ES6.
symbols
can be used ?They bring a few benefits to the language and are particularly useful when used as object properties. But, what can they do for us that strings cannot? Usually, we use strings as object properties.
Symbol()
global factory function.Symbol()
we get a new and unique symbol, guaranteed to be different from all other symbolsSymbol() === Symbol() //false
const NAME = Symbol()
const person = {
[NAME]: 'Jonathan'
}
person[NAME] //'Sumanth'
const RUN = Symbol()
person[RUN] = () => 'Person is running'
console.log(person[RUN]()) //'Person is running'
A value of this type can be created using Symbol()
:
let id = Symbol();
// here id is the new symbol
consolo.log(id); // Symbol()
Since a symbol
is a primitive value, if you attempt to create a symbol using the new operator, you will get an error:
let s = new Symbol(); // TypeError
let arr = new Array(); // Totally acceptable
We can also give symbol a description in string format as a first argument while instantiating a Symbol and it is optional and it doesn't affect the symbol itself.
This description is mostly useful for debugging purpose.
let id = Symbol(); // without description
let id2 = Symbol("firstname"); // symbol with desc "firstname"
console.log(id2); // Symbol("firstname")
console.log(id2.description); // "firstname"
The Symbol()
function creates a new unique value each time you call it:
console.log(Symbol() === Symbol()); // false
const s1 = Symbol();
const s2 = Symbol();
console.log(s1 === s2); // false
Even if we give the same description to different symbols they are totally different and that's why Symbol is a unique Identifier.
let id1 = Symbol("desc");
const str = 'desc';
let id2 = Symbol("desc");
console.log(id1 == id2); // false
console.log(s1 === str); // false
let value1 = "Sumanth"; // primitive type string
let value2 = "Sumanth";
console.log(value1===value2); // true
function primitiveMutator(val) {
return val = val + 1;
}
let x = 1;
primitiveMutator(x); // 2
console.log(x); // 1 (value not changed)
function objectMutator(obj) {
obj.prop = obj.prop + 1;
}
let obj = { prop: 1 };
objectMutator(obj);
console.log(obj.prop); // 2
const first = "abc" + "def";
const second = "ab" + "cd" + "ef";
console.log(first === second); // true
const obj1 = { name: "Intrinsic" };
const obj2 = { name: "Intrinsic" };
console.log(obj1 === obj2); // false
// Though, their .name properties ARE primitives:
console.log(obj1.name === obj2.name); // true
Objects play an elemental role in the JavaScript language. They’re often used as collections of key/value pairs. However, this is a big limitation of using them in this manner: Until symbols existed, object keys could only be strings. If we ever attempt to use a non-string value as a key for an object, the value will be coerced to a string.
NOTE: Map
data structure was created in part to allow for key/value storage in situations where a key is not a string. Whereas in Objects we can use only strings as keys
Symbols
(Symbols actual Use-case)properties
to an Object:let obj = {name:"sumanth"};
obj["Native"] = "Mars"; // using square brackets
console.log(obj["Native"]); // "Mars"
obj.phno = 96584; // using dot operator
console.log(obj.phno) // 96584
console.log(obj); // {name: "sumanth", Native: "Mars", phno: 96584}
let obj = {}; // new object
let sym = Symbol("name"); // new symbol
obj[sym] = 'foo'; // adding new property to obj
obj = {
[sym]:"foobar", // another way of using symbols // not as "sym": 123
[Symbol('name')]: 'foo' // directly creating symbols here
}
What’s the benefit of using Symbol("name") as property name or key over a string "name"?
let id= Symbol('name'); // new symbol
var newObj = {
[id]:369, // symbol property
okay:"A text"
}
JSON.stringify(newObj) // {"okay":"A text"}
for( var prop in newObj) {
console.log(newObj[prop]); // A text
}
console.log(Object.keys( newObj)); // ['okay']
If you can observe in the above examples symbol properties are not appeared when we try to access them. In this way they allow us to create some "HIDDEN" properties of an object.
But these properties are not fully private in a strict sense. We can access them using Object.getOwnPropertySymbols()
Object.getOwnPropertySymbols(newObj)[0] // Symbol(name)
newObj[Object.getOwnPropertySymbols(newObj)[0]]; // 369
object
from another codelet user = { name: "John" }; // imported code
// Our script wants to create "id" property or identifier
user.id = "Our id value";
// ...Another script also wants "id" for its purposes...
user.id = "Their id value"
// Boom! overwritten by another script!
So, to overcome this conflict we can use Symbols as property names.
let user = { // belongs to another code
name: "John"
};
let id = Symbol("id");
user[id] = 1;
Also, imagine that another script wants to have its own identifier inside user
, for its own purposes. That may be another JavaScript library, so that the scripts are completely unaware of each other.
Then that script can create its own Symbol("id")
, like this:
// ...
let id = Symbol("id");
user[id] = "Their id value";
There will be no conflict between our and their identifiers, because symbols are always different, even if they have the same name.
…But if we used a string "id"
instead of a symbol for the same purpose, then there would be a conflict as we seen above.
To avoid conflicts in identifier names we can use Symbols. If one imported object has a property as "name" and if we accidentally create our property with same "name" then the previous property will be overridden.