Khi nào nên sử dụng Bản đồ thay vì Đối tượng JavaScript đơn giản?
Đối tượng JavaScript đơn giản {key: 'value'} chứa dữ liệu có cấu trúc. Nhưng đối tượng JS đơn giản có những hạn chế của nó:
Chỉ các chuỗi và ký hiệu có thể được sử dụng làm khóa của Đối tượng. Nếu chúng ta sử dụng bất kỳ thứ gì khác nói, các số làm khóa của một đối tượng thì trong khi truy cập các khóa đó, chúng ta sẽ thấy các khóa đó sẽ được chuyển đổi thành các chuỗi ngầm khiến chúng ta mất tính nhất quán của các loại. tên const = {1: 'một', 2: 'hai'}; Object.keys (tên); // ['1', '2']
Có khả năng vô tình ghi đè các thuộc tính được kế thừa từ các nguyên mẫu bằng cách viết mã định danh JS làm tên chính của một đối tượng (ví dụ: toString, constructor, v.v.)
Một đối tượng khác không thể được sử dụng làm khóa của một đối tượng, vì vậy không có thông tin bổ sung nào có thể được viết cho một đối tượng bằng cách viết đối tượng đó là khóa của đối tượng khác và giá trị của đối tượng khác sẽ chứa thông tin bổ sung
Đối tượng không phải là trình vòng lặp
Kích thước của một đối tượng không thể được xác định trực tiếp
Những hạn chế này của Đối tượng được giải quyết bằng Bản đồ nhưng chúng ta phải coi Bản đồ là phần bổ sung cho Đối tượng thay vì thay thế. Về cơ bản Map chỉ là mảng các mảng nhưng chúng ta phải truyền mảng đó cho đối tượng Map làm đối số với từ khóa mới, nếu không chỉ với mảng mảng thì các thuộc tính và phương thức hữu ích của Map không có sẵn. Và hãy nhớ các cặp khóa-giá trị bên trong mảng các mảng hoặc Bản đồ phải được phân tách bằng dấu phẩy, không có dấu hai chấm như trong các đối tượng đơn giản.
3 mẹo để quyết định nên sử dụng Bản đồ hay Đối tượng:
Sử dụng bản đồ trên các đối tượng khi các khóa không xác định cho đến khi hết thời gian vì các khóa được tạo bởi đầu vào của người dùng hoặc vô tình có thể phá vỡ mã sử dụng đối tượng nếu các khóa đó ghi đè lên các thuộc tính được kế thừa của đối tượng, vì vậy bản đồ sẽ an toàn hơn trong các trường hợp đó. Cũng sử dụng bản đồ khi tất cả các khóa là cùng loại và tất cả các bản đồ là cùng loại.
Sử dụng bản đồ nếu có nhu cầu lưu trữ các giá trị nguyên thủy làm khóa.
Sử dụng các đối tượng nếu chúng ta cần hoạt động trên các yếu tố cá nhân.
Lợi ích của việc sử dụng Bản đồ là:
1. Bản đồ chấp nhận bất kỳ loại khóa nào và giữ nguyên loại khóa:
Chúng tôi biết rằng nếu khóa của đối tượng không phải là một chuỗi hoặc ký hiệu thì JS sẽ ngầm biến nó thành một chuỗi. Ngược lại, Map chấp nhận bất kỳ loại khóa nào: chuỗi, số, boolean, ký hiệu, v.v. và Map giữ nguyên loại khóa gốc. Ở đây chúng tôi sẽ sử dụng số làm khóa trong Bản đồ và nó sẽ vẫn là một số:
const numbersMap= new Map();
numbersMap.set(1, 'one');
numbersMap.set(2, 'two');
const keysOfMap= [...numbersMap.keys()];
console.log(keysOfMap); // [1, 2]
Trong Bản đồ, chúng ta thậm chí có thể sử dụng toàn bộ một đối tượng làm khóa. Có thể đôi khi chúng ta muốn lưu trữ một số dữ liệu liên quan đến đối tượng, mà không đính kèm dữ liệu này vào bên trong đối tượng để chúng ta có thể làm việc với các đối tượng nạc nhưng muốn lưu trữ một số thông tin về đối tượng. Trong những trường hợp đó, chúng ta cần sử dụng Bản đồ để có thể đặt Đối tượng làm khóa và dữ liệu liên quan của đối tượng làm giá trị.
const foo= {name: foo};
const bar= {name: bar};
const kindOfMap= [[foo, 'Foo related data'], [bar, 'Bar related data']];
Nhưng nhược điểm của phương pháp này là sự phức tạp của việc truy cập giá trị theo khóa, vì chúng ta phải lặp qua toàn bộ mảng để có được giá trị mong muốn.
function getBy Key(kindOfMap, key) {
for (const [k, v] of kindOfMap) {
if(key === k) {
return v;
}
}
return undefined;
}
getByKey(kindOfMap, foo); // 'Foo related data'
Chúng tôi có thể giải quyết vấn đề này khi không truy cập trực tiếp vào giá trị bằng cách sử dụng Bản đồ phù hợp.
const foo= {name: 'foo'};
const bar= {name: 'bar'};
const myMap= new Map();
myMap.set(foo, 'Foo related data');
myMap.set(bar, 'Bar related data');
console.log(myMap.get(foo)); // 'Foo related data'
Chúng ta có thể thực hiện điều này bằng WeakMap, chỉ cần viết, const myMap = new WeakMap (). Sự khác biệt giữa Map và WeakMap là WeakMap cho phép thu gom rác các khóa (ở đây là các đối tượng) để ngăn chặn rò rỉ bộ nhớ, WeakMap chỉ chấp nhận các đối tượng làm khóa và WeakMap đã giảm tập hợp các phương thức.
2. Bản đồ không hạn chế tên chính:
Đối với các đối tượng JS đơn giản, chúng ta có thể vô tình ghi đè lên thuộc tính được kế thừa từ nguyên mẫu và nó có thể nguy hiểm. Ở đây chúng ta sẽ ghi đè lên thuộc tính toString () của đối tượng tác nhân:
const actor= {
name: 'Harrison Ford',
toString: 'Actor: Harrison Ford'
};
Bây giờ, hãy xác định một fn isPlainObject () để xác định xem đối số được cung cấp có phải là một đối tượng đơn giản hay không và fn này sử dụng phương thức toString () để kiểm tra nó:
function isPlainObject(value) {
return value.toString() === '[object Object]';
}
isPlainObject(actor); // TypeError : value.toString is not a function
// this is because inside actor object toString property is a string instead of inherited method from prototype
Bản đồ không có bất kỳ hạn chế nào đối với tên khóa, chúng ta có thể sử dụng các tên khóa như toString, constructor, v.v.
const actorMap= new Map();
actorMap.set('name', 'Harrison Ford');
actorMap.set('toString', 'Actor: Harrison Ford');
function isMap(value) {
return value.toString() === '[object Map]';
}
console.log(isMap(actorMap)); // true
Nếu chúng ta gặp tình huống đầu vào của người dùng tạo các khóa thì chúng ta phải lấy các khóa đó bên trong Bản đồ thay vì một đối tượng đơn giản. Điều này là do người dùng có thể chọn một tên trường tùy chỉnh như, toString, constructor, v.v. sau đó các tên khóa như vậy trong một đối tượng đơn giản có khả năng phá vỡ mã mà sau này sử dụng đối tượng này. Vì vậy, giải pháp phù hợp là liên kết trạng thái giao diện người dùng với bản đồ, không có cách nào để phá vỡ Bản đồ:
const userCustomFieldsMap= new Map([['color', 'blue'], ['size', 'medium'], ['toString', 'A blue box']]);
3. Bản đồ có thể lặp lại:
Để lặp lại các thuộc tính của một đối tượng đơn giản, chúng ta cần Object.entries () hoặc Object.keys (). Object.entries (plainObject) trả về một mảng các cặp giá trị khóa được trích xuất từ đối tượng, sau đó chúng ta có thể phá hủy các khóa và giá trị đó và có thể nhận được các khóa và giá trị đầu ra bình thường.
const colorHex= {
'white': '#FFFFFF',
'black': '#000000'
}
for(const [color, hex] of Object.entries(colorHex)) {
console.log(color, hex);
}
//
'white' '#FFFFFF'
'black' '#000000'
Vì Bản đồ có thể lặp lại, đó là lý do tại sao chúng ta không cần các phương thức entry () để lặp lại trên Bản đồ và phá hủy khóa, mảng giá trị có thể được thực hiện trực tiếp trên Bản đồ vì bên trong Bản đồ, mỗi phần tử tồn tại dưới dạng một mảng các cặp giá trị khóa được phân tách bằng dấu phẩy .
const colorHexMap= new Map();
colorHexMap.set('white', '#FFFFFF');
colorHexMap.set('black', '#000000');
for(const [color, hex] of colorHexMap) {
console.log(color, hex);
}
//'white' '#FFFFFF' 'black' '#000000'
Ngoài ra map.keys () trả về một iterator trên các khóa và map.values () trả về một iterator trên các giá trị.
4. Chúng ta có thể dễ dàng biết kích thước của Bản đồ
Chúng ta không thể xác định trực tiếp số lượng thuộc tính trong một đối tượng đơn giản. Chúng ta cần một trình trợ giúp fn like, Object.keys () trả về một mảng với các khóa của đối tượng sau đó sử dụng thuộc tính length chúng ta có thể lấy số lượng khóa hoặc kích thước của đối tượng đơn giản.
const exams= {'John Rambo': '80%', 'James Bond': '60%'};
const sizeOfObj= Object.keys(exams).length;
console.log(sizeOfObj); // 2
Nhưng trong trường hợp Bản đồ, chúng ta có thể có quyền truy cập trực tiếp vào kích thước của Bản đồ bằng cách sử dụng thuộc tính map.size.
const examsMap= new Map([['John Rambo', '80%'], ['James Bond', '60%']]);
console.log(examsMap.size);