Keyed collections
Chương này giới thiệu các tập hợp dữ liệu được lập chỉ số bằng khóa; các đối tượng Map và Set chứa các phần tử có thể duyệt theo thứ tự chèn vào.
Maps
>Đối tượng Map
Đối tượng Map là một map khóa/giá trị có thể duyệt các phần tử theo thứ tự chèn.
Đoạn code sau trình bày một số thao tác cơ bản với Map. Xem thêm trang tham chiếu Map để biết thêm ví dụ và API đầy đủ. Bạn có thể dùng vòng lặp for...of để trả về mảng [key, value] trong mỗi lần lặp.
const sayings = new Map();
sayings.set("dog", "woof");
sayings.set("cat", "meow");
sayings.set("elephant", "toot");
sayings.size; // 3
sayings.get("dog"); // woof
sayings.get("fox"); // undefined
sayings.has("bird"); // false
sayings.delete("dog");
sayings.has("dog"); // false
for (const [key, value] of sayings) {
console.log(`${key} goes ${value}`);
}
// "cat goes meow"
// "elephant goes toot"
sayings.clear();
sayings.size; // 0
So sánh Object và Map
Theo truyền thống, objects được dùng để ánh xạ chuỗi sang giá trị. Object cho phép bạn đặt khóa thành giá trị, lấy lại giá trị đó, xóa khóa và kiểm tra xem có gì được lưu tại một khóa hay không. Tuy nhiên, Map có thêm một số ưu điểm giúp nó trở thành map tốt hơn.
- Khóa của
Objectlà string hoặc symbol, trong khi khóa củaMapcó thể là bất kỳ giá trị nào. - Bạn có thể dễ dàng lấy
sizecủaMap, trong khi phải theo dõi thủ công kích thước củaObject. - Việc duyệt map theo thứ tự chèn của các phần tử.
Objectcó prototype, vì vậy có các khóa mặc định trong map. (Điều này có thể bỏ qua bằng cách dùngmap = Object.create(null).)
Ba gợi ý sau giúp bạn quyết định nên dùng Map hay Object:
- Dùng map thay object khi các khóa chưa biết cho đến runtime, và khi tất cả khóa có cùng kiểu và tất cả giá trị có cùng kiểu.
- Dùng map nếu cần lưu trữ giá trị primitive làm khóa vì object xử lý mỗi khóa như chuỗi dù đó là giá trị số, boolean hay primitive nào khác.
- Dùng object khi có logic thao tác trên từng phần tử riêng lẻ.
Đối tượng WeakMap
WeakMap là tập hợp các cặp khóa/giá trị mà khóa phải là đối tượng hoặc symbol không đăng ký, với giá trị thuộc bất kỳ kiểu JavaScript tùy ý, và không tạo tham chiếu mạnh đến khóa. Tức là, sự tồn tại của một đối tượng làm khóa trong WeakMap không ngăn đối tượng đó bị garbage collect. Khi đối tượng được dùng làm khóa bị thu gom, các giá trị tương ứng của nó trong bất kỳ WeakMap nào cũng trở thành ứng viên garbage collection — miễn là chúng không được tham chiếu mạnh ở nơi khác. Kiểu primitive duy nhất có thể dùng làm khóa WeakMap là symbol — cụ thể hơn là symbol không đăng ký — vì symbol không đăng ký được đảm bảo là duy nhất và không thể tạo lại.
API của WeakMap về cơ bản giống API của Map. Tuy nhiên, WeakMap không cho phép quan sát sức sống của các khóa, đó là lý do tại sao nó không cho phép enumeration. Vì vậy không có phương thức để lấy danh sách các khóa trong WeakMap. Nếu có, danh sách sẽ phụ thuộc vào trạng thái garbage collection, gây ra tính không xác định.
Để biết thêm thông tin và code ví dụ, xem "Why WeakMap?" trên trang tham chiếu WeakMap.
Một use case của đối tượng WeakMap là lưu trữ dữ liệu private cho đối tượng, hoặc ẩn chi tiết triển khai. Trong ví dụ sau, dữ liệu private và các phương thức nằm bên trong đối tượng và được lưu trong đối tượng privates, đây là WeakMap. Mọi thứ được public trên instance và prototype; mọi thứ khác không thể truy cập từ bên ngoài vì privates không được export từ module.
const privates = new WeakMap();
export default function Public() {
const me = {
// Private data goes here
};
privates.set(this, me);
}
Public.prototype.method = function () {
const me = privates.get(this);
// Do stuff with private data in `me`
// …
};
Note: Use case này hiện có thể được triển khai với classes và private fields.
Sets
>Đối tượng Set
Đối tượng Set là tập hợp các giá trị duy nhất. Bạn có thể duyệt các phần tử theo thứ tự chèn. Một giá trị trong Set chỉ có thể xuất hiện một lần; nó là duy nhất trong tập hợp Set.
Đoạn code sau trình bày một số thao tác cơ bản với Set. Xem thêm trang tham chiếu Set để biết thêm ví dụ và API đầy đủ.
const mySet = new Set();
mySet.add(1);
mySet.add("some text");
mySet.add("foo");
mySet.has(1); // true
mySet.delete("foo");
mySet.size; // 2
for (const item of mySet) {
console.log(item);
}
// 1
// "some text"
Chuyển đổi giữa Array và Set
Bạn có thể tạo Array từ Set bằng Array.from hoặc cú pháp spread. Ngoài ra, hàm tạo Set cũng nhận Array để chuyển đổi theo chiều ngược lại.
Note:
Set lưu trữ giá trị duy nhất — vì vậy bất kỳ phần tử trùng lặp nào từ Array sẽ bị xóa khi chuyển đổi!
Array.from(mySet);
[...mySet2];
mySet2 = new Set([1, 2, 3, 4]);
So sánh Array và Set
Theo truyền thống, tập hợp các phần tử được lưu trong mảng trong JavaScript ở nhiều tình huống. Tuy nhiên, đối tượng Set có một số ưu điểm:
- Xóa phần tử mảng theo giá trị (
arr.splice(arr.indexOf(val), 1)) rất chậm. Setcho phép xóa phần tử theo giá trị. Với mảng, bạn phảisplicedựa trên chỉ số phần tử.- Giá trị
NaNkhông thể tìm thấy bằngindexOftrong mảng. Setlưu trữ các giá trị duy nhất. Bạn không cần theo dõi thủ công các bản sao.
Đối tượng WeakSet
Đối tượng WeakSet là tập hợp các giá trị có thể garbage collect, bao gồm objects và symbol không đăng ký. Một giá trị trong WeakSet chỉ có thể xuất hiện một lần. Nó là duy nhất trong tập hợp WeakSet.
Sự khác biệt chính so với đối tượng Set là:
- Ngược với
Set,WeakSetlà tập hợp chỉ của objects hoặc symbols, không phải giá trị tùy ý thuộc bất kỳ kiểu nào. WeakSetlà weak: Tham chiếu đến các đối tượng trong tập hợp được giữ yếu. Nếu không có tham chiếu khác đến đối tượng được lưu trongWeakSet, chúng có thể bị garbage collect. Điều đó cũng có nghĩa là không có danh sách các đối tượng hiện đang được lưu trong tập hợp.WeakSetkhông thể enumerable.
Các use case của đối tượng WeakSet bị giới hạn. Chúng không làm rò rỉ bộ nhớ, nên có thể an toàn khi dùng các element DOM làm khóa và đánh dấu chúng để theo dõi, chẳng hạn.
Bình đẳng khóa và giá trị của Map và Set
Cả bình đẳng khóa của đối tượng Map lẫn bình đẳng giá trị của đối tượng Set đều dựa trên thuật toán SameValueZero:
- Bình đẳng hoạt động như toán tử so sánh đồng nhất
===. -0và+0được coi là bằng nhau.NaNđược coi là bằng chính nó (ngược với===).