JS宏进阶:Map与Object

Object 是 JavaScript 中最基本的数据类型之一,用于创建对象实例。new Object( )是创建空对象的一种常见方式。而Map只是一种用于存储键值对的数据结构。相对于Object而言,他没有原型(也就是不能通过原型链的方式添加方法),但也存在自身的优势,某些场景,new Map 可能比 new Object 更好用。下面是其内置方法的详细介绍:

一、new Map

1、创建新的Map对象,只能使用new Map构造函数:

const myMap = new Map();

2、set 与 get

这两种方法,在Map对象中:分别是设置键值对,与获取值的方法

//设置键值对
myMap.set('name', 'Alice');
myMap.set('age', 30);

//输出值
console.log(myMap.get('name')); // 输出: Alice
console.log(myMap.get('age'));  // 输出: 30

3、获取所有的键和值

使用keys()方法,可以获取所有的键;使用values()方法获取所有的值;使用entries()方法获取所有的键值对。

myMap.keys()
myMap.values()
myMap.entries()

4、查找某个键或值

可以使用has方法查询Map对象中是否包含某个键。然而,Map中并没有提供相应的方法来查询Map对象中包含某个值,但我们可以自己定义。示例如下:

console.log(myMap.has('name')); // 输出: true
console.log(myMap.has('address')); // 输出: false

//在内置对象Map的原型上添加方法includesValue来查询map对象中是否包含某个值
Map.prototype.includesValue = function(value) {
	let v = JSON.stringify(value);
	for (let key of this.keys()) {
		if (JSON.stringify(this.get(key)) == v) return true;
	}
	return false;
}

5、遍历map对象

map的forEach方法:

myMap.set('name', 'Alice');
myMap.set('age', 30);

myMap.forEach((value, key, map) => {
  console.log(`${key} = ${value}`);
});
// 输出:
// name = Alice
// age = 30

for...of 循环

for (const [key, value] of myMap.entries()) {
  console.log(`${key} = ${value}`);
}
// 输出:
// name = Alice
// age = 30

6、Map中的其他方法

delete:删除键值对

myMap.delete('age');
console.log(myMap.has('age')); // 输出: false

size:统计键值对的数量

console.log(myMap.size); // 输出: 1 (因为已经删除了 'age' 键)

clear:清空map对象中的键值对

myMap.clear();
console.log(myMap.size); // 输出: 0

7、注意事项

  • 键的唯一性:

    Map中的键是唯一的,不允许重复。新的值会覆盖与已存在键相关联的旧值。

  • 键的类型:

    Map的键可以是任何类型的值,包括基本数据类型、对象和函数。

  • 顺序性:

    Map能够记住键的原始插入顺序。

  • 迭代性:

    Map是可迭代的,可以使用 for...of 循环或其他迭代方法遍历其键值对。

二、new Object

前面将对象时已经介绍过,调用它可以创建一个空对象。等效于使用 { }创建空对象。今天我们详细讲解其中的内置方法。

1、实例方法

1.1、hasOwnProperty:判断对象自身是否含有某个属性(非继承自原型链的属性)

let obj = new Object();
obj.name = 'John';
console.log(obj.hasOwnProperty('name')); // 输出: true
console.log(obj.hasOwnProperty('toString')); // 输出: false

1.2、isPrototypeOf:判断一个对象是否存在于另一个对象的原型链上。

let obj = new Object();
console.log(Object.prototype.isPrototypeOf(obj)); // 输出: true

1.3、propertyIsEnumerable:判断对象自身的某个属性是否可枚举(即是否可以在 for...in 循环中被遍历)。

let obj = new Object();
obj.name = 'John';
console.log(obj.propertyIsEnumerable('name')); // 输出: true

1.4、toString/toLocalString:变成字符串

let obj = new Object();
console.log(obj.toString()); // 输出: [object Object]

//本地化的字符串
let obj = new Object();
console.log(obj.toLocaleString()); // 输出: [object Object]

1.5、valueOf:返回对象的原始值。对于普通对象,该方法通常返回对象本身。

let obj = new Object();
console.log(obj.valueOf() === obj); // 输出: true

1.6、根据实际需求,我们时常需要自己定义equals方法,用于判断两个对象是否一致。

Object.prototype.equals = function(other) {
    let keysA = Object.keys(this);
    let keysB = Object.keys(other);
    if (keysA.length !== keysB.length) {
        return false;
    }
    for (let key of keysA) {
        if (this[key] !== other[key]) {
            return false;
        }
    }
    return true;
};

let obj1 = new Object({ a: 1, b: 2 });
let obj2 = new Object({ a: 1, b: 2 });
console.log(obj1.equals(obj2)); // 输出: true

2、静态方法

2.1、preventExtensions:使得对象不可扩展,用于保护对象

let obj = new Object();
Object.preventExtensions(obj);
obj.newProp = 'value'; // 不会添加新属性
console.log(obj.hasOwnProperty('newProp')); // 输出: false

2.2、isExtensible:对象是否可扩展,输出boolean值

let obj = new Object();
console.log(Object.isExtensible(obj)); // 输出: true
Object.preventExtensions(obj);
console.log(Object.isExtensible(obj)); // 输出: false

2.3、seal:使对象变得不可扩展,并标记所有现有属性为不可配置。这意味着不能删除现有属性,也不能更改属性的配置属性(如 writableconfigurable)。

let obj = new Object();
obj.name = 'John';
Object.seal(obj);
obj.age = 30; // 不会添加新属性
delete obj.name; // 不会删除现有属性
console.log(obj.hasOwnProperty('name')); // 输出: true
console.log(obj.hasOwnProperty('age')); // 输出: false

2.4、isSealed:对象是否已封闭

let obj = new Object();
console.log(Object.isSealed(obj)); // 输出: false
Object.seal(obj);
console.log(Object.isSealed(obj)); // 输出: true

2.5、freeze:全方位冻结对象,也就是使对象变得不可扩展,标记所有现有属性为不可配置,并将所有现有属性冻结(即不可写)。

let obj = new Object();
obj.name = 'John';
Object.freeze(obj);
obj.age = 30; // 不会添加新属性
delete obj.name; // 不会删除现有属性
obj.name = 'Jane'; // 不会更改现有属性
console.log(obj.name); // 输出: John

2.6、isFrozen:判断对象是否被冻结

let obj = new Object();
console.log(Object.isFrozen(obj)); // 输出: false
Object.freeze(obj);
console.log(Object.isFrozen(obj)); // 输出: true

2.7、keys/values/entries:作用与Map对象中的相同

let obj = new Object();
obj.name = 'John';
obj.age = 30;

//获取键
Object.keys(obj); // 结果是: ['name', 'age']

//获取值
Object.values(obj); // 结果是: ['John', 30]

//获取键值对
Object.entries(obj) // 结果是:[['name', 'John'], ['age', 30]]

2.8、create:创建一个新对象,使用现有的对象作为原型,并可选择性地定义其属性。在 函数、对象和类(二)中介绍过其作用

let obj = Object.create(Object.prototype, {
    name: {
        value: 'John',
        writable: true,
        enumerable: true,
        configurable: true
    }
});
console.log(obj.name); // 输出: John

2.9、assign:将所有可枚举属性的值从一个或多个源对象复制到目标对象。返回目标对象。

let obj1 = new Object();
let obj2 = { name: 'John', age: 30 };
Object.assign(obj1, obj2);
console.log(obj1); // 输出: { name: 'John', age: 30

三、new Map 与 new Object 的异同

1、相同点

键值对存储:两者都可以用来存储键值对。

动态增删:两者都可以动态地增加、删除键值对。

2、不同点

2.1、键的类型:

Object :  键只能是字符串或 Symbol。

Map:  键可以是任何类型,包括对象、函数、原始数据类型等。

const obj = {};
obj[1] = 'number key'; // 实际会被转换成字符串 "1"
console.log(obj["1"]); // "number key"

const map = new Map();
map.set(1, 'number key');
console.log(map.get(1)); // "number key"

2.2、顺序:

Object : 键值对的顺序通常是创建时的顺序,但在某些情况下(如整数类型的键)可能不完全可靠。

Map: 保持插入顺序,可以可靠地迭代键值对。

const obj = { b: 1, a: 2, 3: 3 };
console.log(Object.keys(obj)); // ["3", "b", "a"] (顺序可能不固定)

const map = new Map();
map.set('b', 1);
map.set('a', 2);
map.set(3, 3);
console.log([...map.keys()]); // ["b", "a", 3] (顺序保持)

2.3、性能:

Object : 在频繁添加和删除属性时性能可能较差,因为对象属性涉及更多的内部机制(如原型链查找)。

Map: 在频繁增删操作中通常性能更好,因为 Map 是专门为键值对存储设计的。

2.4、原型链:

Object : 对象有原型链,可以通过原型链继承属性和方法。

Map: 没有原型链,更简洁,不会受到原型链上的意外干扰。

2.5、大小和遍历:

Object : 获取对象的大小需要使用Object.keys(obj).length

Map: 可以直接使用size获取大小。

遍历方面:

Object : 使用 for...in 循环,但可能会遍历到原型链上的属性。

Map: 使用 for...of 循环,配合entries,或者forEach方法,遍历更清晰、更安全。

四、使用场景推荐

Map:需要保持键值对的插入顺序。需要使用非字符串类型作为键。需要频繁地进行增删查改操作,且数据集较大。

Object:简单的键值对存储,且键为字符串或 Symbol。需要简洁的语法和点语法访问属性。需要与 JSON 数据进行交互。

五、总结

总的来说,new Map( ) 与 new Object( ) 都是JS中非常重要的对象,学会并理解他们的使用场景和优势,可以帮助你在开发中选择最合适的数据结构,提高代码的效率和可维护性。

你可能感兴趣的:(JS宏进阶,javascript,开发语言,ecmascript)