openplanning

Hướng dẫn và ví dụ JavaScript Symbol

  1. Khái niệm về Symbol
  2. Symbol - Key
  3. Symbol - Enums
  4. Symbol.for(key) & Symbol.forKey(symbol)

1. Khái niệm về Symbol

Symbol là một "kiểu dữ liệu nguyên thủy" (Primitive data type) mới, được giới thiệu trong ECMAScript 6. Trước ES6 chúng ta chỉ có 5 kiểu nguyên thủy là Number, String, Boolean, null, undefined.
Để tạo ra một Symbol bạn sử dụng cú pháp dưới đây:
// Create a Symbol
var mySymbol1  = Symbol();

// Create a Symbol
var mySymbol2 = Symbol("Something");
Chú ý rằng: Symbol không phải là một lớp, vì vậy bạn không thể tạo nó bằng toán tử new:
var mySymbol1 = new Simbol(); // ==> ERROR!!

var mySymbol2 = new Simbol("Something"); // ==> ERROR!!
Tất cả các kiểu nguyên thủy Number, String, Boolean, null, undefined đều rất rõ ràng và tường minh.
  • Number: 1, 2, 3, ...
  • String: "Hello", "Bye", ...
  • Boolean: true, false
  • null
  • undefined
Symbol là một thứ trừu tượng, bạn không thể sờ vào được nó, và không thể biết giá trị thực tế của nó là gì. Symbol là hoàn toàn duy nhất (Hoàn toàn khác nhau), điều đó có nghĩa là nếu bạn tạo ra 2 Symbol, thì chúng là khác nhau, kể cả khi bạn tạo ra 2 Symbol theo cùng một cách.
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 có thể sử dụng như một khóa (Key) cho các đối tượng 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 có thể được sử dụng như một property của một đối tượng.
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
Symbol thực sự có ích để bạn định nghĩa metadata (Siêu dữ liệu) trong một đối tượng. Nếu property (của một đối tượng) là một Symbol, nó sẽ không được nhận biết bởi các hàm trả về các 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
}
Các Symbol Property không hoàn toàn ẩn, bạn vẫn có thể lấy ra danh sách các Symbol Property của một đối tượng thông qua các phương thức sau:
Object.getOwnPropertySymbols(someObject);

Reflect.ownKeys(someObject);
Ví dụ:
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

Trong ES5 rất thường xuyên bạn phải tạo ra các hằng số (constants) để đại diện cho khái niệm nào đó. Và kiểu dữ liệu thường được sử dụng để định nghĩa hằng số là Number hoặc String.
OK, Chẳng hạn bạn cần tạo ra 4 hằng số đại diện cho 4 mùa của năm, và hàm getWeather(season) trả về thời tiết tương ứng với mùa được truyền vào.
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
Đôi khi bạn sử dụng sai hằng số trong code nhưng vẫn được chương trình chấp nhận. Điều này thật nguy hiểm.
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
Sử dụng Symbol để định nghĩa các hằng số là một giải pháp tốt cho bạn trong trường hợp này. Các hằng số kiểu Symbol đại diện cho một khái niệm cụ thể nào đó được gọi là các Enums (Giống với khái niệm Enums trong 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)

Phương thức Symbol.for(keyName) trả về một giá trị là một Symbol tương ứng với khóa keyName trong một đối tượng Map toàn cục (Global). Nếu khóa keyName không tồn tại trong đối tượng Map toàn cục, một cặp keyName/Symbol(keyName) sẽ được thêm vào đối tượng Map, và trả về Symbol(keyName) nói trên.
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) là giống nhau.
symbol-for-example2.js
const foo = Symbol.for();

const bar = Symbol.for(undefined);

console.log( foo === bar); // true
Nếu một Symbol đang được quản lý trên đối tượng Map toàn cục, bạn có thể tìm được khóa của nó bằng cách sử dụng phương thức 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

Các hướng dẫn ECMAScript, Javascript

Show More