Theo tài liệu của Javascript, các tên(từ khoá) thuộc tính đối tượng có thể là kiểu chuỗi hoặc kiểu ký hiệu(Symbol). Không phải số, không phải booleans, chỉ có chuỗi hoặc ký hiệu, hai loại này.

Cho đến bây giờ chúng ta chỉ sử dụng các chuỗi. Bây giờ hãy xem những lợi ích mà các biểu tượng(Symbol) có thể mang lại cho chúng ta.

1. Biểu tượng(Symbol)

Một biểu tượng sẽ đại diện cho một định danh duy nhất.

Một giá trị của kiểu này có thể được tạo bằng cách sử dụng Symbol():

// id is a new symbol
let id = Symbol();

Khi tạo, chúng ta có thể cung cấp cho biểu tượng một mô tả (còn được gọi là tên biểu tượng), chủ yếu hữu ích cho mục đích gỡ lỗi:

// id is a symbol with the description "id"
let id = Symbol("id");

Biểu tượng(Symbol) được đảm bảo là duy nhất. Ngay cả khi chúng ta tạo nhiều biểu tượng với cùng một mô tả, chúng là các giá trị khác nhau. Mô tả chỉ là một nhãn hiệu không ảnh hưởng đến bất cứ điều gì.

Chẳng hạn, đây là hai biểu tượng có cùng mô tả – chúng không bằng nhau:

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/


let id1 = Symbol("id");
let id2 = Symbol("id");

alert(id1 == id2); // false

Nếu bạn đã quen thuộc với Ruby hoặc một số ngôn ngữ khác cũng có một số kiểu biểu tượng này(symbol) – thì xin đừng nhầm lẫn. Biểu tượng JavaScript là khác nhau. Các biểu tượng không tự động chuyển đổi thành một chuỗi

Hầu hết các giá trị trong JavaScript đều hỗ trợ chuyển đổi ngầm định thành một chuỗi. Ví dụ, chúng ta có thể alertgần như bất kỳ giá trị nào, và nó sẽ hoạt động. Biểu tượng(Symbol) là đặc biệt. Họ không tự động chuyển đổi.

Chẳng hạn, điều này alertsẽ hiển thị một lỗi:

let id = Symbol("id");
alert(id); // TypeError: Cannot convert a Symbol value to a string

Đó là một bảo vệ của ngôn ngữ, chống lại sự lộn xộn, bởi vì các chuỗi và biểu tượng khác nhau về cơ bản và không nên vô tình chuyển đổi cái này thành cái khác.

Nếu chúng ta thực sự muốn hiển thị một biểu tượng, chúng ta cần gọi .toString()nó một cách rõ ràng, như ở đây:

let id = Symbol("id");
alert(id.toString()); // Symbol(id), now it works

Hoặc lấy thuộc tính symbol.description để chỉ hiển thị mô tả:

let id = Symbol("id");
alert(id.description); // id

2. Thuộc tính Hidden

Các biểu tượng cho phép chúng ta tạo ra các thuộc tính ẩn giấu của một đối tượng, mà không một phần code nào khác có thể vô tình truy cập hoặc ghi đè.

Chẳng hạn, nếu chúng ta làm việc với các đối tượng user, thuộc về code của bên thứ ba. Chúng ta muốn thêm định danh cho họ.

Hãy sử dụng một phím biểu tượng cho nó:

let user = { // belongs to another code
  name: "John"
};

let id = Symbol("id");

user[id] = 1;

alert( user[id] ); // we can access the data using the symbol as the key

Lợi ích của việc sử dụng Symbol("id")thay cho một chuỗi "id"gì?

usercác đối tượng thuộc về một code khác và code đó cũng hoạt động với chúng, chúng ta không nên thêm bất kỳ trường nào vào nó. Điều đó không an toàn. Nhưng một biểu tượng không thể được truy cập một cách tình cờ, code của bên thứ ba thậm chí sẽ không nhìn thấy nó, vì vậy có lẽ không sao cả.

Ngoài ra, hãy tưởng tượng rằng một tập lệnh khác muốn có định danh riêng bên trong user, cho mục đích riêng của nó. Đó có thể là một thư viện JavaScript khác, do đó các tập lệnh hoàn toàn không biết về nhau.

Sau đó, kịch bản có thể tạo riêng của nó một Symbol("id"), như thế này:

// ...
let id = Symbol("id");

user[id] = "Their id value";

Sẽ không có xung đột giữa chúng ta và định danh của họ, bởi vì các biểu tượng luôn khác nhau, ngay cả khi chúng có cùng tên.

Nhưng nếu chúng ta sử dụng một chuỗi "id"thay vì một biểu tượng cho cùng một mục đích, thì sẽ có một xung đột:

let user = { name: "John" };

// Our script uses "id" property
user.id = "Our id value";

// ...Another script also wants "id" for its purposes...

user.id = "Their id value"
// Boom! overwritten by another script!

3. Biểu tượng trong một chữ

Nếu chúng ta muốn sử dụng một biểu tượng trong một đối tượng bằng chữ(literal) {...}, chúng ta cần dấu ngoặc vuông xung quanh nó.

Như thế này:

let id = Symbol("id");

let user = {
  name: "John",
  [id]: 123 // not "id: 123"
};

Đó là bởi vì chúng ta cần giá trị từ biến idlàm từ khóa chứ không phải chuỗi “id”.

4. Các biểu tượng được bỏ qua trong for …in

Symbolic không hoạt động với vòng lặp. for..in

Ví dụ:

let id = Symbol("id");
let user = {
  name: "David",
  age: 30,
  [id]: 123
};

for (let key in user) alert(key); // name, age (no symbols)

// the direct access by the symbol works
alert( "Direct: " + user[id] );

Object.keys(user)cũng bỏ qua chúng. Đó là một phần của nguyên tắc biểu tượng ẩn giấu. Nếu một tập lệnh khác hoặc một thư viện lặp lại đối tượng của chúng ta, nó sẽ không truy cập vào một thuộc tính Symbolic.

Ngược lại, Object.assign sao chép cả hai thuộc tính chuỗi và Symbolic:

let id = Symbol("id");
let user = {
  [id]: 123
};

let clone = Object.assign({}, user);

alert( clone[id] ); // 123

Không có nghịch lý ở đây. Đó là do thiết kế. Ý tưởng là khi chúng ta sao chép một đối tượng hoặc hợp nhất các đối tượng, chúng ta thường muốn tất cả các thuộc tính được sao chép (bao gồm các biểu tượng như id).

5. Biểu tượng toàn Cục

Như chúng ta đã thấy, thông thường tất cả các biểu tượng đều khác nhau, ngay cả khi chúng có cùng tên. Nhưng đôi khi chúng ta muốn các biểu tượng cùng tên là cùng một thực thể. Chẳng hạn, các phần khác nhau trong ứng dụng của chúng ta muốn truy cập biểu tượng "id"có nghĩa chính xác là cùng một thuộc tính.

Để đạt được điều đó, tồn tại một đăng ký biểu tượng toàn cục(global symbol registry) . Chúng ta có thể tạo các biểu tượng trong đó và truy cập chúng sau này và nó đảm bảo rằng các truy cập lặp lại có cùng tên trả về chính xác cùng một ký hiệu.

Để đọc một biểu tượng, hãy sử dụng Symbol.for(key).

Cái này gọi để kiểm tra biểu tượng toàn cục và nếu có một biểu tượng được mô tả là key, sau đó trả lại nó, nếu không sẽ tạo một biểu tượng mới Symbol(key)và lưu nó vào sổ đăng ký bằng cách cho key.

Ví dụ:

// read from the global registry
let id = Symbol.for("id"); // if the symbol did not exist, it is created

// read it again (maybe from another part of the code)
let idAgain = Symbol.for("id");

// the same symbol
alert( id === idAgain ); // true

Các biểu tượng bên trong sổ đăng ký được gọi là biểu tượng toàn cầu. Nếu chúng ta muốn một biểu tượng trên toàn ứng dụng, có thể truy cập ở mọi nơi trong code

Nghe có vẻ giống Ruby

Trong một số ngôn ngữ lập trình, như Ruby, có một ký hiệu cho mỗi tên.

Trong JavaScript, như chúng ta có thể thấy, điều đó phù hợp với các biểu tượng toàn cục.

6. Symbol.keyFor

Đối với các biểu tượng toàn cục, không chỉ Symbol.for(key)trả về một biểu tượng theo tên, mà còn có một cuộc gọi ngược lại : Symbol.keyFor(sym), thực hiện ngược lại: trả về một tên bằng một biểu tượng toàn cục.

Ví dụ:

// get symbol by name
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");

// get name by symbol
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id

Symbol.keyFor Nội bộ sử dụng sổ đăng ký biểu tượng toàn cục để tra cứu từ khóa cho biểu tượng. Vì vậy, nó không hoạt động cho các biểu tượng phi toàn cục. Nếu biểu tượng không phải là toàn cục, nó sẽ không thể tìm thấy và trả lại undefined.

Điều đó nói rằng, bất kỳ biểu tượng có thuộc tính description.

Ví dụ:

let globalSymbol = Symbol.for("name");
let localSymbol = Symbol("name");

alert( Symbol.keyFor(globalSymbol) ); // name, global symbol
alert( Symbol.keyFor(localSymbol) ); // undefined, not global

alert( localSymbol.description ); // name

7. Symbols hệ thống

Tồn tại nhiều biểu tượng của hệ thống mà JavaScript sử dụng bên trong và chúng ta có thể sử dụng chúng để tinh chỉnh các khía cạnh khác nhau của các đối tượng.

Chúng được liệt kê trong đặc tả trong bảng biểu tượng nổi tiếng:

  • Symbol.hasInstance
  • Symbol.isConcatSpreadable
  • Symbol.iterator
  • Symbol.toPrimitive
  • …và như thế.

Chẳng hạn, Symbol.toPrimitivecho phép chúng ta mô tả đối tượng để chuyển đổi nguyên thủy. Chúng ta sẽ thấy việc sử dụng nó ở các bài sau.

Các biểu tượng khác cũng sẽ trở nên quen thuộc khi chúng ta nghiên cứu các tính năng tương ứng khác trong ngôn ngữ này.

8. Tóm lược

Symbol là một kiểu nguyên thủy cho các định danh duy nhất.

Biểu tượng được tạo bằng cách dùng Symbol() với một mô tả tùy chọn (tên).

Biểu tượng luôn có giá trị khác nhau, ngay cả khi chúng có cùng tên. Nếu chúng ta muốn các biểu tượng cùng tên bằng nhau, thì chúng ta nên sử dụng sổ đăng ký toàn cầu: Symbol.for(key)trả về (tạo nếu cần) một biểu tượng toàn cầu với tên như key. Nhiều cuộc gọi Symbol.forvới cùng một keytrả lại chính xác cùng một biểu tượng.

Biểu tượng có hai trường hợp sử dụng chính:

  1. Thuộc tính đối tượng ẩn giấu. Nếu chúng ta muốn thêm một thuộc tính vào một đối tượng mà thuộc về một tập lệnh hoặc thư viện khác, chúng ta có thể tạo một biểu tượng và sử dụng nó làm khóa thuộc tính. Một thuộc tính tượng trưng không xuất hiện for..in, vì vậy nó sẽ không được xử lý ngẫu nhiên cùng với các thuộc tính khác. Ngoài ra, nó sẽ không được truy cập trực tiếp, bởi vì tập lệnh khác không có biểu tượng của chúng tôi. Vì vậy, thuộc tính sẽ được bảo vệ khỏi việc sử dụng ngẫu nhiên hoặc ghi đè. Vì vậy, chúng ta có thể có một cách tình cờ, giấu một thứ gì đó vào các đối tượng mà chúng ta cần, nhưng những người khác không nên nhìn thấy, sử dụng các thuộc tính tượng trưng.
  2. Có nhiều biểu tượng hệ thống được sử dụng bởi JavaScript có thể truy cập như Symbol.*. Chúng ta có thể sử dụng chúng để thay đổi một số hành vi tích hợp. Ví dụ, sau này trong hướng dẫn, chúng tôi sẽ sử dụng Symbol.iteratorcho các lần lặp , Symbol.toPrimitiveđể thiết lập chuyển đổi từ đối tượng sang nguyên thủy , v.v.

Về mặt kỹ thuật, các biểu tượng không bị ẩn 100%. Có một phương thức tích hợp Object.getOwnPropertySymbols (obj) cho phép chúng ta có được tất cả các biểu tượng. Ngoài ra, có một phương thức có tên Reflect.ownKeys (obj) trả về tất cả các khóa của một đối tượng bao gồm cả các biểu tượng. Vì vậy, họ không thực sự ẩn. Nhưng hầu hết các thư viện, hàm dựng sẵn và cú pháp cấu trúc không sử dụng các phương thức này.

Full series tự học Javascript từ cơ bản tới nâng cao tại đây nha.

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!