在JavaScript中,普通对象({})
和 Map
对象都是用于存储键值对的数据结构,但是他们有一些区别。
1. 键的类型
普通对象: 对象的键必须是字符串或 Symbol
类型。其他类型的值(如数字、布尔值、对象等)会被强制转换为字符串。例如,1 和 "1" 在对象中是同一个键。
Map: Map
允许任何类型的值作为键,包括对象、函数、原始类型等。例如,1 和 "1" 在 Map 中是不同的键。
2. 键的顺序
普通对象: 在ES6之前,对象的键没有特定的顺序。但在现代JavaScript中,对象的字符串键按插入顺序排序,数字键按升序排序,Symbol 键的顺序未定义。
Map: Map 保留键的插入顺序,当你遍历 Map 时,键会按照插入的顺序被访问。
让我们考虑一个带有电话号码的对象:
javascript
let codes = {
49: "Germany",
41: "Switzerland",
44: "Great Britain",
// ..,
1: "USA",
};
for (let code in codes) {
console.log(code); // 1, 41, 44, 49
}
console.log(Object.keys(codes)); //[ '1', '41', '44', '49' ]
不论是in
操作符,还是Object.keys
,键的顺序是按照整数顺序排列的,同时Object.values
,也是对应的key
的顺序的
javascript
console.log(Object.keys(codes)); //[ '1', '41', '44', '49' ]
console.log(Object.values(codes)); //[ 'USA', 'Switzerland', 'Great Britain', 'Germany' ]
即 整数属性会被进行排序,其他属性则按照创建的顺序显示。
所谓整数属性 指的是一个可以在不做任何更改的情况下与一个整数进行相互转换的字符串。
非整数属性
如果属性名不是整数,那它们就按照创建时的顺序来排序,例如:
javascript
let user = {
name: "John",
surname: "Smith",
};
user.age = 25; // 增加一个
// 非整数属性是按照创建的顺序来排列的
for (let prop in user) {
console.log(prop); // name, surname, age
}
console.log(Object.keys(user)); //[ 'name', 'surname', 'age' ]
两者都有
既有整数属性也有非整数属性
javascript
let user = {
name: "John",
surname: "Smith",
49: "Germany",
41: "Switzerland",
44: "Great Britain",
1: "USA",
};
user.age = 25; // 增加一个
user["0"] = 23; // 增加一个
// 既有整数属性也有非整数属性
// 1.整数属性会按照数字顺序排序,
// 2.非整数熟悉按照插入顺序
// 3.先输出的是整数属性
for (let prop in user) {
console.log(prop); // 0,1,41,44,49,name, surname, age
}
console.log(Object.keys(user)); //[ '0', '1', '41', '44', '49', 'name', 'surname', 'age' ]
3.默认键
普通对象 :普通对象继承自 Object.prototype
,因此默认带有一些预定义的键 (如 toString
、hasOwnProperty
)。虽然这些键可以被覆盖,但在使用时需要注意可能的冲突。
javascript
const obj = { age: 20 };
obj.__proto__.test = "haha";
for (const k in obj) {
console.log(k);
/*
age 自身的
test 和原型对象的可枚举键
*/
}
console.log(Object.keys(obj)); // ['age']
console.log(Object.getPrototypeOf(obj));
注意:
for...in 循环只会迭代可枚举 的非符号属性。从内置构造函数(如 Array 和 Object)创建的对象会从Array.prototype 和 Object.prototype 继承不可枚举属性,例如 Array 的 indexOf() 方法或Object 的 toString() 方法,它们在 for...in 循环中不会被访问到。
Map:Map 默认不包含任何键。它只包含显式存入的键值对。因此,Map 对象不会与任何原型链上的属性冲突。
javascript
const map = new Map();
map.set("name", "zs"); //自有键
map.set("age", 20); //自有键
Object.getPrototypeOf(map).test = "haha"; //原型对象的键
console.log(map.keys()); //MapIterator {'name', 'age'}
4.迭代性
普通对象 :迭代对象的键需要使用 Object.keys()、Object.values()
或 Object.entries()
。对象本身并不是可迭代的(没有实现 Symbol.iterator
接口)。for...in
也可以遍历对象
for...in 不能直接遍历 Map 对象。它通常用于遍历普通对象的属性,而不是 Map 对象的键值对。
javascript
const map = new Map();
map.set("name", "zs"); //自有键
map.set("age", 20); //自有键
for (const key in map) {
console.log(key); //没有任何输出
}
要遍历 Map 对象,map是可迭代的,使用for...of
或者
javascript
map.forEach() / map.keys() / map.values() / map.entries()
5.序列化
普通对象 :对象可以使用 JSON.stringify()
进行序列化,结果是一个JSON字符串。
Map :Map 不能 直接通过 JSON.stringify()
进行序列化,因为它不是一个普通的对象。如果需要序列化 Map,需要先将其转换为数组或对象。
普通对象的序列化
javascript
const obj = {
name: "Alice",
age: 30,
city: "New York",
};
const jsonString = JSON.stringify(obj);
console.log(jsonString); // {"name":"Alice","age":30,"city":"New York"}
Map 对象的序列化
Map 对象不能直接用 JSON.stringify()
进行序列化。如果你尝试这样做,结果会是一个空对象 {}。
javascript
const map = new Map();
map.set("name", "Alice");
map.set("age", 30);
map.set("city", "New York");
const jsonString = JSON.stringify(map);
console.log(jsonString); // {}
为了正确序列化 Map
对象,你需要先将 Map
转换为可以被 JSON.stringify()
识别的格式,如数组或对象。
转换为普通对象
javascript
const map = new Map();
map.set("name", "Alice");
map.set("age", 30);
map.set("city", "New York");
const mapObject = Object.fromEntries(map);
const jsonStringFromObject = JSON.stringify(mapObject);
console.log(jsonStringFromObject); // {"name":"Alice","age":30,"city":"New York"}
反序列化
对于 Map 对象,先将 JSON
字符串转换为数组或对象,再转换为 Map。
javascript
const map = new Map();
map.set("name", "Alice");
map.set("age", 30);
map.set("city", "New York");
// 序列化
const mapObject = Object.fromEntries(map);
const jsonStringFromObject = JSON.stringify(mapObject);
console.log(jsonStringFromObject); // {"name":"Alice","age":30,"city":"New York"}
// 反序列化
// 1.先将 JSON 字符串转换为数组或对象
const parsedObject = JSON.parse(jsonStringFromObject);
// 2.再转换为 Map
const parsedMapFromObject = new Map(Object.entries(parsedObject));
console.log(parsedMapFromObject.get("name")); // "Alice"
6.使用场景
普通对象 :适用于管理少量数据,并且键是已知的、简单的字符串或符号的情况下。
Map:适用于需要灵活、动态键(特别是非字符串键),或者需要高效查找和操作大量键值对的场景。