What is the motivation for bringing Symbols to ES6?

JavascriptSymbolsEcmascript HarmonyEcmascript 6

Javascript Problem Overview


>UPDATE: Recently a brilliant article from Mozilla came up. Read it if you're curious.

As you may know they are planning to include new Symbol primitive type in ECMAScript 6 (not to mention some other crazy stuff). I always thought that the :symbol notion in Ruby is needless; we could easily use plain strings instead, like we do in JavaScript. And now they decide to complicate things in JS with that.

I don't understand the motivation. Could someone explain to me whether we really need symbols in JavaScript?

Javascript Solutions


Solution 1 - Javascript

The original motivation for introducing symbols to Javascript was to enable private properties.

Unfortunately, they ended up being severely downgraded. They are no longer private, since you can find them via reflection, for example, using Object.getOwnPropertySymbols or proxies.

They are now known as unique symbols and their only intended use is to avoid name clashes between properties. For example, ECMAScript itself can now introduce extension hooks via certain methods that you can put on objects (e.g. to define their iteration protocol) without risking them to clash with user names.

Whether that is strong enough a motivation to add symbols to the language is debatable.

Solution 2 - Javascript

Symbols do not guarantee true privacy but can be used to separate public and internal properties of objects. Let's take an example where we can use Symbol for having private properties.

Let's take an example where a property of an object is not private.

var Pet = (function() {
  function Pet(type) {
    this.type = type;
  }
  Pet.prototype.getType = function() {
    return this.type;
  }
  return Pet;
}());

var a = new Pet('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Modified outside
console.log(a.getType());//Output: null

Above, the Pet class property type is not private. To make it private we have to create a closure. The below example illustrates how we can make type private using a closure.

var Pet = (function() {
  function Pet(type) {
    this.getType = function(){
      return type;
    };
  }
  return Pet;
}());

var b = new Pet('dog');
console.log(b.getType());//dog
b.type = null;
//Stays private
console.log(b.getType());//dog

Disadvantage of above approach: We are introducing an extra closure for each Pet instance created, which can harm performance.

Now we introduce Symbol. This can help us make a property private without using extra unnecessary closures. Code example below:

var Pet = (function() {
  var typeSymbol = Symbol('type');
  function Pet(type) {
    this[typeSymbol] = type;
  }
  Pet.prototype.getType = function(){
    return this[typeSymbol];
  }
  return Pet;
}());

var a = new Pet('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Stays private
console.log(a.getType());//Output: dog

Solution 3 - Javascript

Symbols are a new, special kind of object that can be used as a unique property name in objects. Using symbols instead of a strings allows different modules to create properties that don’t conflict with one another. Symbols can also be made effectively private, so that their properties can’t be accessed by anyone who doesn’t already have direct access to the symbol.

Symbols are a new primitive, just like the number, string, and boolean primitives. Unlike the other primitives, symbols do not have a literal syntax (e.g. how string has '') — the only way to create them is with the Symbol constructor in the following way:

let symbol = Symbol();

In reality, symbols are just a slightly different way to attach properties to an object — you could easily provide the well-known symbols as standard methods, just like Object.prototype.hasOwnProperty, which appears in everything that inherits from Object.

Here are some of the benefits of the Symbol primitive type.

Symbols have debuggability built in

Symbols can be given a description, which is really just used for debugging to make life a little easier when logging them to a console.

Symbols can be used as object keys

This is where symbols get really interesting. They are heavily intertwined with objects. Symbols can be assigned as keys to objects, meaning you can assign an unlimited number of unique symbols to an object and be guaranteed that these will never conflict with string keys, or other unique symbols.

Symbols can be used as unique values

Let’s assume you have a logging library, which includes multiple log levels such as logger.levels.DEBUG, logger.levels.INFO, logger.levels.WARN and so on. In ES5 code you’d like make these strings (so logger.levels.DEBUG === 'debug'), or numbers (logger.levels.DEBUG === 10). Both of these aren’t ideal as those values aren’t unique values, but symbols are! So logger.levels simply becomes:

log.levels = {
  DEBUG: Symbol('debug'),
  INFO: Symbol('info'),
  WARN: Symbol('warn'),
};
log(log.levels.DEBUG, 'debug message');
log(log.levels.INFO, 'info message');

Read more in this great article.

Solution 4 - Javascript

This post is about the Symbol(), supplied with actual examples I could find/make and facts & definitions I could find.

TLDR;

The Symbol() is the data type, introduced with the release of ECMAScript 6 (ES6).

There're two curious facts about the Symbol.

  • the first data type and only data type in JavaScript which has got no literal

  • any variable, defined with Symbol(), gets unique content, but it's not really private.

  • any data has its own Symbol, and for the same data the Symbols would be the same. More info in the following paragraph, otherwise it's not a TLRD; :)

How do I initialise the symbol?

1. To get a unique identifier with a debuggable value

You can do it either this way:

var mySymbol1 = Symbol();

Or this way:

var mySymbol2 = Symbol("some text here");

The "some text here" string can't be extracted from the symbol, it's just a description for debugging purposes. It doesn't change the behaviour of symbol in any way. Although, you could console.log it (which is fair, since the value is for debugging, so as not to mistake that log with some other log entry):

console.log(mySymbol2);
// Symbol(some text here)
2. To obtain a symbol for some string data

In this case the value of symbol is actually taken into account and this way two symbols may be non-unique.

var a1 = Symbol.for("test");
var a2 = Symbol.for("test");
console.log(a1 == a2); //true!

Let's call those symbols "second-type" symbols. They do not intersect with the "first-type" symbols (i.e. the ones defined with Symbol(data)) in any way.

The next two paragraphs pertain only the first-type symbol.

How do I benefit from using Symbol instead of the older data types?

Let's first consider an object, a standard data type. We could define some key-values pairs there and have an access to the values by specifying the key.

var persons = {"peter":"pan","jon":"doe"};
console.log(persons.peter);
// pan

What if we have two persons with the name Peter?

Doing this:

var persons = {"peter":"first", "peter":"pan"};

wouldn't make much sense.

So, appears to be a problem of two absolutely different persons having a same name. Let's then refer out new Symbol(). It's like a person in real life - any person is unique, but their names can be equal. Let's define two "persons".

 var a = Symbol("peter");
 var b = Symbol("peter");

Now we have got two different persons with the same name. Are our persons different indeed? They are; you can check this:

 console.log(a == b);
 // false

How do we benefit there?

We can make two entries in your object for the different persons and they can't be mistaken in any way.

 var firstPerson = Symbol("peter");
 var secondPerson = Symbol("peter");
 var persons = {[firstPerson]:"first", [secondPerson]:"pan"};

>Note:
>It's worth to notice, though, that stringifying the object with JSON.stringify will drop all the pairs initialised with a Symbol as a key.
>Executing Object.keys won't either return such Symbol()->value pairs.

Using this initialisation, it's absolutely impossible to mistake the entries for the first and second persons. Calling console.log for them will correctly output their second names.

 console.log(persons[a]);
 // first
 console.log(persons[b]);
 // pan

When used in object, how it is different compared to defining non-enumerable property?

Indeed, there already existed a way to define a property to be hidden from Object.keys and enumeration. Here it is:

var anObject = {};
var fruit = "apple";    

Object.defineProperty( anObject, fruit, {
    enumerable: false,
    value: "green"
});

What difference does Symbol() bring there? The difference is that you can still get the property defined with Object.defineProperty in the usual way:

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //green
console.log(anObject.apple); //green

And if defined with Symbol as in previous paragraph:

fruit = Symbol("apple");

You will have an ability to receive its value only if knowing its variable, i.e.

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //undefined
console.log(anObject.apple); //undefined

Moreover, defining another property under the key "apple" will make the object drop the older one (and if hard-coded, it could throw an error). So, no more apples! That's a pity. Referring the previous paragraph, the Symbols are unique and defining a key as Symbol() will make it unique.

Type conversion and checking

  • Unlike other data types, it's impossible to convert the Symbol() to any other data type.

  • It's possible to "make" a symbol based on primitive data type by calling Symbol(data).

  • In terms of checking the type, nothing changes.

     function isSymbol ( variable ) {
         return typeof someSymbol === "symbol";
     }
    
     var a_Symbol = Symbol("hey!");
     var totally_Not_A_Symbol = "hey";
    
     console.log(isSymbol(a_Symbol)); //true
     console.log(isSymbol(totally_Not_A_Symbol)); //false
    

Solution 5 - Javascript

Here is how I see it. Symbols provide 'an extra level of privacy', by preventing the keys/properties of an object from being exposed through some popular methods such as Object.keys() and JSON.stringify().

var age = Symbol();  // declared in another module perhaps?
class Person {
   constructor(n,a){
      this.name = n;
      this[age] = a;  
   }
   introduce(){
       console.log(`My name is ${this.name}. I am ${this[age]-10}.`);
   }
}
var j = new Person('Jane',45);
j.introduce();  // My name is Jane. I am 35.
console.log(JSON.stringify(j)); // {"name":"Jane"}
console.log(Object.keys(j)); // ["name"]
console.log(j[age]); // 45   (well…only if you know the age in the first place…)

Although given an object per se, such properties can still be exposed through reflection, proxy, Object.getOwnPropertySymbols() etc., there is no natural means to access them through a few direct methods, which may be sufficient sometimes from an OOP perspective.

Solution 6 - Javascript

A JS symbol is a new primitive data type. They are tokens that serve as unique IDs. A symbol can be created using the Symbol constructor. Take for instance this snippet from MDN:

// The symbol constructor takes one optional argument, 
// the descriptions which is used for debugging only.
// Here are two symbols with the same description
let Sym1 = Symbol("Sym");
let Sym2 = Symbol("Sym");
  
console.log(Sym1 == Sym2); // returns "false"
// Symbols are guaranteed to be unique.
// Even if we create many symbols with the same description,
// they are different values.

It is often handy to use symbols as unique object property keys, for example:

let obj = {};
let prop = Symbol();

obj[prop] = 123;  // the symbol prop is assigned 123
obj.prop  = 456;  // the string prop is assigned 456

console.log(obj.prop, obj[prop]); // logs 456, 123

Solution 7 - Javascript

> Symbols have two main use cases: > > 1. “Hidden” object properties. If we want to add a property into an object that “belongs” to another script or a library, we can create a > symbol and use it as a property key. A symbolic property does not > appear in for..in, so it won’t be accidentally processed together > with other properties. Also it won’t be accessed directly, because > another script does not have our symbol. So the property will be > protected from accidental use or overwrite. > > So we can “covertly” hide something into objects that we need, but > others should not see, using symbolic properties. > > 2. There are many system symbols used by JavaScript which are accessible as Symbol.*. We can use them to alter some built-in > behaviors. For instance, ...... > Symbol.iterator for iterables, Symbol.toPrimitive to setup > object-to-primitive conversion and so on.

Source

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYanisView Question on Stackoverflow
Solution 1 - JavascriptAndreas RossbergView Answer on Stackoverflow
Solution 2 - JavascriptSamar PandaView Answer on Stackoverflow
Solution 3 - JavascriptMihai Alexandru-IonutView Answer on Stackoverflow
Solution 4 - JavascriptnicaelView Answer on Stackoverflow
Solution 5 - JavascriptChong Lip PhangView Answer on Stackoverflow
Solution 6 - JavascriptWillem van der VeenView Answer on Stackoverflow
Solution 7 - JavascriptsnrView Answer on Stackoverflow