devstory

Le Tutoriel de ECMAScript Symbol

Suivez-nous sur notre fanpage pour recevoir des notifications chaque fois qu'il y a de nouveaux articles. Facebook

1- Concept de Symbol

Symbol est un nouveau "type de données primitives" (Primitive data type), introduit en ECMAScript 6. Avant ES6 nous avons 5 types primatifs: Number, String, Boolean, null, undefined.
Afin de créer un Symbol vous pouvez utiliser la syntaxe ci-dessous :

// Create a Symbol
var mySymbol1  = Symbol();

// Create a Symbol
var mySymbol2 = Symbol("Something");
Notez que Symbol n'est pas une classe donc vous ne pouvez pas le créer par l'opérateur new :


var mySymbol1 = new Simbol(); // ==> ERROR!!

var mySymbol2 = new Simbol("Something"); // ==> ERROR!!
Tous les types primatifs tels que Number, String, Boolean, null, undefined sont très clairs et explicites.
  • Number: 1, 2, 3, ...
  • String: "Hello", "Bye", ...
  • Boolean: true, false
  • null
  • undefined
Symbol est une abstraction. Vous ne pouvez ni le toucher ni savoir ce qui est sa valeur réelle. Le Symbol est complètement unique (complètement différent), ce qui signifie que si vous créez deux Symbol, ils sont différents, même si vous créez deux Symbol de la même manière.
symbol-example1.js


var symbolA1 = Symbol();
var symbolA2 = Symbol();

console.log(symbolA1); // Symbol()
console.log(symbolA2); // Symbol()
console.log(symbolA1 === symbolA2); // false

var symbolB1 = Symbol("Tom");
var symbolB2 = Symbol("Tom");

console.log(symbolB1); // Symbol(Tom)
console.log(symbolB2); // Symbol(Tom)
console.log(symbolB1 === symbolB2); // false
 

2- Symbol - Key

Symbol peut être utilisé comme une clé (Key) des objets Map.
symbol-map-key-example.js


var key1 = Symbol();
var key2 = Symbol();
var key3 = Symbol("Something");

var map = new Map();

map.set(key1, "Tom");
map.set("a_string_key", "Donald");
map.set(key2, "Jerry");
map.set(key3, "Mickey");

console.log( map.get(key1));  // Tom
console.log( map.get("a_string_key")); // Donald
console.log( map.get(key2));  // Jerry
console.log( map.get(key3));  // Mickey
 
[Symbol]?
Symbol peut être utilisé comme une property d'un objet.
symbol-object-property-example.js


var prop1 = Symbol();
var prop2 = Symbol();
var prop3 = Symbol("Something");

var myObject = {
   name : "Tom",
   gender: "Male",
   [prop1]: "Something 1",
   [prop2]: "Something 2",
   [prop3]: "Something 3",
};

console.log( myObject["name"] );  // Tom
console.log( myObject["gender"] ); // Male

console.log( myObject[prop1] ); // Something 1
Le Symbol est vraiment utile lors que vous définissez metadata (métadonnées) dans un objet. Si la property (d'un objet) est un Symbol, elle ne sera pas reconnue pas les fonctions qui renvoie des property.
symbol-metadata-example.js

const sym = Symbol()

const foo = {
  name: 'Tom',
  age: 25,
  [sym]: 'Some Hidden Metadata'
}

let keys = Object.keys(foo) // name, age
console.log("keys: " + keys);

let propNames = Object.getOwnPropertyNames(foo) // name, age
console.log("propNames: " + propNames);

for(let val of keys) {
  console.log(foo[val]) // Tom // 25
}
Les Symbol Property ne sont pas complètement cachés. Vous pouvez encore faire une liste des Symbol Property d'un objet via les méthodes ci-dessous :


Object.getOwnPropertySymbols(someObject);

Reflect.ownKeys(someObject);
 
Exemple :
symbol-get-props-example.js

const sym1 = Symbol();
const sym2 = Symbol("Test");

const someObject = {
  name: 'Tom',
  age: 25,
  [sym1]: 'Some Hidden Metadata 1',
  [sym2]: 'Some Hidden Metadata 2'
}

var symbolProps = Object.getOwnPropertySymbols(someObject);

console.log(symbolProps); // [ Symbol(), Symbol(Test) ]

var objKeys = Reflect.ownKeys(someObject);

console.log(objKeys); // [ 'name', 'age', Symbol(), Symbol(Test) ]
 

3- Symbol - Enums

En ES5 vous devez créer des constants fréquemment afin de représenter un concept. Et les données sont normalement utilisées pour définir  un constant est Number ou String.
OK, par exemple, vous devez créer quatre constants pour représenter les quatre saisons d'un an, et la fonction getWeather(season) renvoie le temps qui correspond à la saison transmise.

var SEASON_SPRING = "SPRING";
var SEASON_SUMMER = "SUMMER";
var SEASON_AUTUMN = "AUTUMN";
var SEASON_WINTER = "WINTER";

function getWeather(season)  {
   switch(season) {
      case SEASON_SPRING:
         return "warm";
      case SEASON_SUMMER:
         return "hot";
      case SEASON_AUTUMN:
         return "cool";
      case SEASON_WINTER:
          return "cold";
      default:
         throw 'Invalid season';
   }
}

console.log( getWeather(SEASON_SPRING) ); // warm
Parfois, vous utilisez des constantes incorrectement dans le code mais cela est toujours accepté par le programme. Pourtant, c'est dangereux.

var SEASON_SPRING = "SPRING";
var SEASON_SUMMER = "SUMMER";
var SEASON_AUTUMN = "AUTUMN";
var SEASON_WINTER = "WINTER";

var FRAMEWORK_SPRING = "SPRING";
var FRAMEWORK_STRUTS = "STRUTS";


var weather1 = getWeather( SEASON_SPRING ); // warm

// (***)
var weather2 = getWeather( FRAMEWORK_SPRING ); // warm
L'utilisation de Symbol pour définir des constants est une bonne solution pour vous dans ce cas. Les constants du type Symbol qui représentent un concept précis sont nommés Enums (similaire au concept Enums en Java).
symbol-enums-example.js

// Season Enums:
const SEASON_SPRING = Symbol();
const SEASON_SUMMER = Symbol();
const SEASON_AUTUMN = Symbol();
const SEASON_WINTER = Symbol();

// Framework Enums:
const FRAMEWORK_SPRING = Symbol();
const FRAMEWORK_STRUTS = Symbol();

function getWeather(season)  { // Season Enums
   switch(season) {
      case SEASON_SPRING:
         return "warm";
      case SEASON_SUMMER:
         return "hot";
      case SEASON_AUTUMN:
         return "cool";
      case SEASON_WINTER:
          return "cold";
      default:
         throw 'Invalid season';
   }
}

console.log( getWeather(SEASON_SPRING) ); // warm

console.log( getWeather(FRAMEWORK_SPRING) ); // Throw Error: Invalid season

4- Symbol.for(key) & Symbol.forKey(symbol)

La méthode Symbol.for(keyName) renvoie une valeur comme un Symbol correspondant à la clé keyName dans un objet Map global (Global). Si la clé keyName n'existe pas dans l'objet Map global, une paire keyName/Symbol(keyName) sera ajoutée à l'objet Map, et renvoie le Symbol(keyName) mentionné au-desous.
symbol-for-example.js


const tom = Symbol.for('Tom') // If the Symbol does not exist, it's created

const tom2 = Symbol.for('Tom') // The Symbol exists, so it is returned

console.log( tom === tom2); // true
 
Symbol.for() & Symbol.for(undefined) sont les même.
symbol-for-example2.js


const foo = Symbol.for();

const bar = Symbol.for(undefined);

console.log( foo === bar); // true
Si un Symbol est géré sur l'objet Map global, vous pouvez trouver sa clé en utilisant la méthode Symbol.keyFor(symbol).
symbol-keyFor-example.js


const foo = Symbol.for('someKey');// This Symbol in Global Map.

const key1 = Symbol.keyFor(foo); // someKey
console.log(key1); // someKey


const bar = Symbol("Test");// This Symbol not in Global Map.

const key2 = Symbol.keyFor(bar);
console.log(key2); // undefined