Map
Baseline
Widely available
*
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
* Some parts of this feature may have varying levels of support.
Đối tượng Map lưu trữ các cặp key-value và ghi nhớ thứ tự chèn ban đầu của các key.
Bất kỳ giá trị nào (cả đối tượng lẫn giá trị nguyên thủy) đều có thể được dùng làm key hoặc value.
Try it
const map = new Map();
map.set("a", 1);
map.set("b", 2);
map.set("c", 3);
console.log(map.get("a"));
// Expected output: 1
map.set("a", 97);
console.log(map.get("a"));
// Expected output: 97
console.log(map.size);
// Expected output: 3
map.delete("b");
console.log(map.size);
// Expected output: 2
Mô tả
Các đối tượng Map là tập hợp các cặp key-value. Một key trong Map chỉ có thể xuất hiện một lần; nó là duy nhất trong tập hợp của Map. Một đối tượng Map được duyệt theo các cặp key-value — một vòng lặp for...of trả về một mảng 2 phần tử [key, value] cho mỗi lần lặp. Việc duyệt xảy ra theo thứ tự chèn, tương ứng với thứ tự mỗi cặp key-value được chèn vào map lần đầu tiên bởi phương thức set() (tức là, không có key nào có cùng giá trị đã có trong map khi set() được gọi).
Đặc tả yêu cầu các map phải được triển khai "trung bình, cung cấp thời gian truy cập ít hơn tuyến tính so với số phần tử trong tập hợp". Do đó, nó có thể được biểu diễn nội bộ dưới dạng bảng băm (với tra cứu O(1)), cây tìm kiếm (với tra cứu O(log(N))), hoặc bất kỳ cấu trúc dữ liệu nào khác, miễn là độ phức tạp tốt hơn O(N).
Bình đẳng key
Bình đẳng giá trị dựa trên thuật toán SameValueZero. (Trước đây sử dụng SameValue, coi 0 và -0 là khác nhau. Kiểm tra khả năng tương thích trình duyệt.) Điều này có nghĩa là NaN được coi là giống NaN (mặc dù NaN !== NaN) và tất cả các giá trị khác được coi là bằng nhau theo ngữ nghĩa của toán tử ===. Ngoài ra, đối với các key là đối tượng, bình đẳng dựa trên danh tính đối tượng. Chúng được so sánh theo tham chiếu, không phải theo giá trị. Xem Sử dụng đối tượng Map để biết ví dụ.
Object so với Map
Object tương tự Map — cả hai đều cho phép bạn đặt key thành các giá trị, truy xuất các giá trị đó, xóa key, và phát hiện xem có gì đó được lưu trữ tại một key hay không. Vì lý do này (và vì không có sẵn các lựa chọn thay thế tích hợp), Object đã được dùng như Map trong lịch sử.
Tuy nhiên, có những sự khác biệt quan trọng khiến Map được ưa thích trong một số trường hợp:
| Map | Object | |
|---|---|---|
| Key ngoài ý muốn |
Một Map không chứa bất kỳ key nào theo mặc định. Nó chỉ
chứa những gì được đặt vào một cách tường minh.
|
Một
Lưu ý: Điều này có thể được khắc phục bằng cách sử dụng
|
| Bảo mật |
Một Map an toàn khi sử dụng với các key và value do người dùng cung cấp.
|
Đặt các cặp key-value do người dùng cung cấp lên một |
| Kiểu Key |
Key của một Map có thể là bất kỳ giá trị nào (bao gồm các hàm,
đối tượng, hoặc bất kỳ giá trị nguyên thủy nào).
|
Key của một Object phải là
String hoặc Symbol.
|
| Thứ tự Key |
Các key trong |
Mặc dù các key của một
Thứ tự lần đầu tiên được xác định chỉ cho các thuộc tính riêng trong ECMAScript
2015; ECMAScript 2020 xác định thứ tự cho các thuộc tính kế thừa. Nhưng lưu ý rằng không có cơ chế đơn lẻ nào
duyệt
tất cả các thuộc tính của một đối tượng; các cơ chế khác nhau
mỗi cái bao gồm các tập con thuộc tính khác nhau.
( |
Kích thước |
Số lượng mục trong một Map dễ dàng lấy được từ thuộc tính
size của nó.
|
Xác định số lượng mục trong một Object phức tạp hơn và kém hiệu quả hơn. Một cách phổ biến để làm điều đó là thông qua length của mảng trả về từ Object.keys().
|
| Duyệt |
Một Map là
iterable, vì vậy nó có thể được duyệt trực tiếp.
|
Lưu ý:
|
| Hiệu năng |
Thực hiện tốt hơn trong các kịch bản liên quan đến việc thêm và xóa các cặp key-value thường xuyên. |
Không được tối ưu hóa cho việc thêm và xóa các cặp key-value thường xuyên. |
| Tuần tự hóa và phân tích cú pháp |
Không có hỗ trợ gốc cho tuần tự hóa hoặc phân tích cú pháp.
(Nhưng bạn có thể xây dựng hỗ trợ tuần tự hóa và phân tích cú pháp riêng cho
|
Hỗ trợ gốc cho tuần tự hóa từ
Hỗ trợ gốc cho phân tích cú pháp từ JSON sang |
Đặt thuộc tính đối tượng
Việc đặt thuộc tính đối tượng cũng hoạt động với các đối tượng Map và có thể gây ra nhầm lẫn đáng kể.
Do đó, đây có vẻ hoạt động theo một cách:
const wrongMap = new Map();
wrongMap["bla"] = "blaa";
wrongMap["bla2"] = "blaaa2";
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
Nhưng cách đặt thuộc tính đó không tương tác với cấu trúc dữ liệu Map. Nó sử dụng tính năng của đối tượng generic. Giá trị của 'bla' không được lưu trong Map cho các truy vấn. Các thao tác khác trên dữ liệu thất bại:
wrongMap.has("bla"); // false
wrongMap.delete("bla"); // false
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
Cách sử dụng đúng để lưu trữ dữ liệu trong Map là thông qua phương thức set(key, value).
const contacts = new Map();
contacts.set("Jessie", { phone: "213-555-1234", address: "123 N 1st Ave" });
contacts.has("Jessie"); // true
contacts.get("Hilary"); // undefined
contacts.set("Hilary", { phone: "617-555-4321", address: "321 S 2nd St" });
contacts.get("Jessie"); // {phone: "213-555-1234", address: "123 N 1st Ave"}
contacts.delete("Raymond"); // false
contacts.delete("Jessie"); // true
console.log(contacts.size); // 1
Các Web API giống Map trong trình duyệt
Các đối tượng giống Map trong trình duyệt (hay "các đối tượng maplike") là các giao diện Web API hoạt động theo nhiều cách giống như một Map.
Giống như Map, các mục có thể được duyệt theo cùng thứ tự chúng được thêm vào đối tượng.
Các đối tượng giống Map và Map cũng có các thuộc tính và phương thức có cùng tên và hành vi.
Tuy nhiên, không giống Map, chúng chỉ cho phép các kiểu được xác định trước cụ thể cho các key và value của mỗi mục.
Các kiểu được phép được đặt trong định nghĩa IDL của đặc tả.
Ví dụ, RTCStatsReport là một đối tượng giống Map phải dùng string cho key và đối tượng cho value.
Điều này được định nghĩa trong IDL đặc tả bên dưới:
interface RTCStatsReport {
readonly maplike<DOMString, object>;
};
Các đối tượng giống Map có thể chỉ đọc hoặc đọc-ghi (xem từ khóa readonly trong IDL ở trên).
- Các đối tượng giống
Mapchỉ đọc có thuộc tínhsize, và các phương thức:entries(),forEach(),get(),has(),keys(),values(), vàSymbol.iterator(). - Các đối tượng giống
Mapcó thể ghi bổ sung có các phương thức:clear(),delete(), vàset().
Các phương thức và thuộc tính có hành vi giống như các thực thể tương đương trong Map, ngoại trừ hạn chế về kiểu của key và value.
Sau đây là các ví dụ về đối tượng giống Map chỉ đọc trong trình duyệt:
Constructor
Map()-
Tạo một đối tượng
Mapmới.
Thuộc tính tĩnh
Map[Symbol.species]-
Hàm constructor được dùng để tạo các đối tượng dẫn xuất.
Phương thức tĩnh
Map.groupBy()-
Nhóm các phần tử của một iterable đã cho bằng cách sử dụng các giá trị được trả về bởi một callback function được cung cấp.
Mapcuối cùng được trả về sử dụng các giá trị duy nhất từ hàm kiểm tra làm key, có thể được dùng để lấy mảng phần tử trong mỗi nhóm.
Thuộc tính instance
Các thuộc tính này được định nghĩa trên Map.prototype và được chia sẻ bởi tất cả các instance Map.
Map.prototype.constructor-
Hàm constructor đã tạo đối tượng instance. Đối với các instance
Map, giá trị khởi đầu là constructorMap. Map.prototype.size-
Trả về số lượng cặp key/value trong đối tượng
Map. Map.prototype[Symbol.toStringTag]-
Giá trị khởi đầu của thuộc tính
[Symbol.toStringTag]là chuỗi"Map". Thuộc tính này được dùng trongObject.prototype.toString().
Phương thức instance
Map.prototype.clear()-
Xóa tất cả các cặp key-value khỏi đối tượng
Map. Map.prototype.delete()-
Xóa mục được chỉ định bởi key khỏi
Mapnày. Map.prototype.entries()-
Trả về một đối tượng Iterator mới chứa mảng hai phần tử
[key, value]cho mỗi phần tử trong đối tượngMaptheo thứ tự chèn. Map.prototype.forEach()-
Gọi
callbackFnmột lần cho mỗi cặp key-value có trong đối tượngMap, theo thứ tự chèn. Nếu tham sốthisArgđược cung cấp choforEach, nó sẽ được dùng làm giá trịthischo mỗi callback. Map.prototype.get()-
Trả về giá trị tương ứng với key trong
Mapnày, hoặcundefinednếu không có. Map.prototype.getOrInsert()-
Trả về giá trị tương ứng với key được chỉ định trong
Mapnày. Nếu key không có mặt, nó chèn một mục mới với key và giá trị mặc định đã cho, và trả về giá trị được chèn. Map.prototype.getOrInsertComputed()-
Trả về giá trị tương ứng với key được chỉ định trong
Mapnày. Nếu key không có mặt, nó chèn một mục mới với key và giá trị mặc định được tính từ một callback đã cho, và trả về giá trị được chèn. Map.prototype.has()-
Trả về một boolean cho biết liệu có một mục với key được chỉ định tồn tại trong
Mapnày hay không. Map.prototype.keys()-
Trả về một đối tượng Iterator mới chứa các key cho mỗi phần tử trong đối tượng
Maptheo thứ tự chèn. Map.prototype.set()-
Thêm một mục mới với key và value được chỉ định vào
Mapnày, hoặc cập nhật một mục hiện có nếu key đã tồn tại. Map.prototype.values()-
Trả về một đối tượng Iterator mới chứa các value cho mỗi phần tử trong đối tượng
Maptheo thứ tự chèn. Map.prototype[Symbol.iterator]()-
Trả về một đối tượng Iterator mới chứa mảng hai phần tử
[key, value]cho mỗi phần tử trong đối tượngMaptheo thứ tự chèn.
Ví dụ
>Sử dụng đối tượng Map
const myMap = new Map();
const keyString = "a string";
const keyObj = {};
const keyFunc = () => {};
// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");
console.log(myMap.size); // 3
// getting the values
console.log(myMap.get(keyString)); // "value associated with 'a string'"
console.log(myMap.get(keyObj)); // "value associated with keyObj"
console.log(myMap.get(keyFunc)); // "value associated with keyFunc"
console.log(myMap.get("a string")); // "value associated with 'a string'", because keyString === 'a string'
console.log(myMap.get({})); // undefined, because keyObj !== {}
console.log(myMap.get(() => {})); // undefined, because keyFunc !== () => {}
Dùng NaN làm key của Map
NaN cũng có thể được dùng làm key. Mặc dù mỗi NaN
không bằng chính nó (NaN !== NaN là đúng), ví dụ sau hoạt động vì
các NaN không thể phân biệt với nhau:
const myMap = new Map();
myMap.set(NaN, "not a number");
myMap.get(NaN);
// "not a number"
const otherNaN = Number("foo");
myMap.get(otherNaN);
// "not a number"
Duyệt Map với for...of
Map có thể được duyệt bằng vòng lặp for...of:
const myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
for (const [key, value] of myMap) {
console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one
for (const key of myMap.keys()) {
console.log(key);
}
// 0
// 1
for (const value of myMap.values()) {
console.log(value);
}
// zero
// one
for (const [key, value] of myMap.entries()) {
console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one
Duyệt Map với forEach()
Map có thể được duyệt bằng phương thức
forEach():
myMap.forEach((value, key) => {
console.log(`${key} = ${value}`);
});
// 0 = zero
// 1 = one
Quan hệ với các đối tượng Array
const kvArray = [
["key1", "value1"],
["key2", "value2"],
];
// Use the regular Map constructor to transform a 2D key-value Array into a map
const myMap = new Map(kvArray);
console.log(myMap.get("key1")); // "value1"
// Use Array.from() to transform a map into a 2D key-value Array
console.log(Array.from(myMap)); // Will show you exactly the same Array as kvArray
// A succinct way to do the same, using the spread syntax
console.log([...myMap]);
// Or use the keys() or values() iterators, and convert them to an array
console.log(Array.from(myMap.keys())); // ["key1", "key2"]
Sao chép và hợp nhất Map
Giống như Array, Map có thể được sao chép:
const original = new Map([[1, "one"]]);
const clone = new Map(original);
console.log(clone.get(1)); // one
console.log(original === clone); // false (useful for shallow comparison)
Note:
Hãy lưu ý rằng dữ liệu bản thân không được sao chép. Nói cách khác, đây chỉ là bản sao nông của Map.
Map có thể được hợp nhất, duy trì tính duy nhất của key:
const first = new Map([
[1, "one"],
[2, "two"],
[3, "three"],
]);
const second = new Map([
[1, "uno"],
[2, "dos"],
]);
// Merge two maps. The last repeated key wins.
// Spread syntax essentially converts a Map to an Array
const merged = new Map([...first, ...second]);
console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
Map cũng có thể được hợp nhất với các Array:
const first = new Map([
[1, "one"],
[2, "two"],
[3, "three"],
]);
const second = new Map([
[1, "uno"],
[2, "dos"],
]);
// Merge maps with an array. The last repeated key wins.
const merged = new Map([...first, ...second, [1, "un"]]);
console.log(merged.get(1)); // un
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
Đặc tả kỹ thuật
| Specification |
|---|
| ECMAScript® 2027 Language Specification> # sec-map-objects> |