容器对象

Map和Set

map

Map 对象是 JavaScript 中的一种集合数据结构,用于存储键值对。与对象(Object)不同,Map 允许任何类型的值作为键,而不仅仅是字符串或符号。Map 也提供了更多的内置方法来操作键值对,使得它在处理键值对集合时更加灵活和高效。

创建 Map 对象

1
let myMap = new Map();

常用方法

1. set(key, value)

  • 用途:向 Map 中添加一个新的键值对。
  • 示例
    1
    2
    myMap.set("key1", "value1");
    myMap.set("key2", "value2");

2. get(key)

  • 用途:根据键获取对应的值。如果键不存在,返回 undefined
  • 示例
    1
    2
    console.log(myMap.get("key1")); // "value1"
    console.log(myMap.get("key3")); // undefined

3. has(key)

  • 用途:检查 Map 中是否存在指定的键。返回 truefalse
  • 示例
    1
    2
    console.log(myMap.has("key1")); // true
    console.log(myMap.has("key3")); // false

4. delete(key)

  • 用途:从 Map 中删除指定的键值对。返回 true 表示删除成功,false 表示键不存在。
  • 示例
    1
    2
    console.log(myMap.delete("key1")); // true
    console.log(myMap.has("key1")); // false

5. clear()

  • 用途:清空 Map 中的所有键值对。
  • 示例
    1
    2
    myMap.clear();
    console.log(myMap.size); // 0

6. size

  • 用途:返回 Map 中键值对的数量。
  • 示例
    1
    2
    3
    myMap.set("key1", "value1");
    myMap.set("key2", "value2");
    console.log(myMap.size); // 2

遍历 Map 对象

Map 对象提供了多种遍历方法,包括 for...of 循环、forEach 方法等。

1. for...of 循环

  • 用途:遍历 Map 中的每个键值对。
  • 示例
    1
    2
    3
    4
    5
    6
    for (let [key, value] of myMap) {
    console.log(key, value);
    }
    // 输出:
    // key1 value1
    // key2 value2

2. forEach 方法

  • 用途:遍历 Map 中的每个键值对,并对每个键值对执行指定的函数。
  • 示例
    1
    2
    3
    4
    5
    6
    myMap.forEach((value, key) => {
    console.log(key, value);
    });
    // 输出:
    // key1 value1
    // key2 value2

键和值的迭代器

Map 对象还提供了 keys()values()entries() 方法,分别用于获取键、值和键值对的迭代器。

1. keys()

  • 用途:返回一个包含所有键的迭代器。
  • 示例
    1
    2
    3
    4
    5
    6
    for (let key of myMap.keys()) {
    console.log(key);
    }
    // 输出:
    // key1
    // key2

2. values()

  • 用途:返回一个包含所有值的迭代器。
  • 示例
    1
    2
    3
    4
    5
    6
    for (let value of myMap.values()) {
    console.log(value);
    }
    // 输出:
    // value1
    // value2

3. entries()

  • 用途:返回一个包含所有键值对的迭代器。
  • 示例
    1
    2
    3
    4
    5
    6
    for (let [key, value] of myMap.entries()) {
    console.log(key, value);
    }
    // 输出:
    // key1 value1
    // key2 value2

示例:使用 Map 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let myMap = new Map();
// 添加键值对
myMap.set("name", "John");
myMap.set("age", 30);
myMap.set("isStudent", false);
// 获取值
console.log(myMap.get("name")); // "John"
console.log(myMap.get("age")); // 30
// 检查键是否存在
console.log(myMap.has("isStudent")); // true
// 删除键值对
myMap.delete("isStudent");
console.log(myMap.has("isStudent")); // false
// 遍历 Map
for (let [key, value] of myMap) {
console.log(key, value);
}
// 输出:
// name John
// age 30
// 清空 Map
myMap.clear();
console.log(myMap.size); // 0

总结

Map 对象是 JavaScript 中处理键值对集合的强大工具,提供了丰富的方法来操作和遍历键值对。与普通对象相比,Map 允许任何类型的值作为键,并且提供了更多的内置方法,使得它在处理复杂数据结构时更加灵活和高效。

map和object的区别

MapObject 都是 JavaScript 中用于存储键值对的数据结构,但它们在功能、性能和使用场景上有一些显著的区别。以下是对 MapObject 的详细对比:

1. 键的类型

  • Object
    • 键的类型:键必须是字符串或符号(Symbol)。其他类型的键会被转换为字符串。
    • 示例
      1
      2
      3
      4
      let obj = {};
      obj[1] = "one"; // 键 1 被转换为字符串 "1"
      obj[true] = "true"; // 键 true 被转换为字符串 "true"
      console.log(obj); // { '1': 'one', 'true': 'true' }
  • Map
    • 键的类型:键可以是任何类型的值,包括对象、函数、数组等。
    • 示例
      1
      2
      3
      4
      5
      6
      let map = new Map();
      map.set(1, "one");
      map.set(true, "true");
      map.set("key", "value");
      map.set({}, "object");
      console.log(map); // Map(4) { 1 => 'one', true => 'true', 'key' => 'value', {} => 'object' }

2. 键的顺序

  • Object
    • 键的顺序:键的顺序是根据键的类型和插入顺序来决定的。字符串键按插入顺序排序,而数字键按升序排序。
    • 示例
      1
      2
      3
      4
      5
      6
      let obj = {};
      obj[10] = "ten";
      obj[2] = "two";
      obj[1] = "one";
      obj["key"] = "value";
      console.log(Object.keys(obj)); // ['1', '2', '10', 'key']
  • Map
    • 键的顺序:键的顺序严格按照插入顺序排列。
    • 示例
      1
      2
      3
      4
      5
      6
      let map = new Map();
      map.set(10, "ten");
      map.set(2, "two");
      map.set(1, "one");
      map.set("key", "value");
      console.log([...map.keys()]); // [10, 2, 1, 'key']

3. 性能

  • Object
    • 性能:在频繁添加和删除键值对时,性能可能会下降,因为 Object 的键是字符串,需要进行哈希映射。
    • 示例
      1
      2
      3
      4
      5
      let obj = {};
      for (let i = 0; i < 1000000; i++) {
      obj[i] = i;
      }
      delete obj[500000];
  • Map
    • 性能:在频繁添加和删除键值对时,性能更稳定。Map 专门设计用于高效地处理键值对,特别是在键的类型多样时。
    • 示例
      1
      2
      3
      4
      5
      let map = new Map();
      for (let i = 0; i < 1000000; i++) {
      map.set(i, i);
      }
      map.delete(500000);

4. 方法和属性

  • Object
    • 方法和属性Object 提供了一些方法和属性,如 Object.keys()Object.values()Object.entries() 等,但这些方法返回的是数组,需要额外的处理。
    • 示例
      1
      2
      3
      4
      let obj = { a: 1, b: 2, c: 3 };
      console.log(Object.keys(obj)); // ['a', 'b', 'c']
      console.log(Object.values(obj)); // [1, 2, 3]
      console.log(Object.entries(obj)); // [['a', 1], ['b', 2], ['c', 3]]
  • Map
    • 方法和属性Map 提供了丰富的内置方法,如 set()get()has()delete()clear()size 等,这些方法和属性直接操作键值对,使用起来更方便。
    • 示例
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      let map = new Map();
      map.set("a", 1);
      map.set("b", 2);
      map.set("c", 3);
      console.log(map.size); // 3
      console.log(map.has("a")); // true
      console.log(map.get("a")); // 1
      map.delete("a");
      console.log(map.has("a")); // false
      map.clear();
      console.log(map.size); // 0

5. 遍历

  • Object
    • 遍历:使用 for...in 循环遍历对象的键。
    • 示例
      1
      2
      3
      4
      5
      6
      7
      8
      let obj = { a: 1, b: 2, c: 3 };
      for (let key in obj) {
      console.log(key, obj[key]);
      }
      // 输出:
      // a 1
      // b 2
      // c 3
  • Map
    • 遍历:使用 for...of 循环遍历 Map 的键值对,也可以使用 forEach 方法。
    • 示例
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      let map = new Map();
      map.set("a", 1);
      map.set("b", 2);
      map.set("c", 3);
      for (let [key, value] of map) {
      console.log(key, value);
      }
      // 输出:
      // a 1
      // b 2
      // c 3
      map.forEach((value, key) => {
      console.log(key, value);
      });
      // 输出:
      // a 1
      // b 2
      // c 3

6. 总结

  • Object
    • 用途:适合存储键为字符串或符号的键值对,适用于简单的数据结构。
    • 优点:语法简洁,广泛使用。
    • 缺点:键的类型有限,性能在频繁操作时可能下降,方法返回的是数组,需要额外处理。
  • Map
    • 用途:适合存储键为任意类型的键值对,特别是在键的类型多样且需要高效操作时。
    • 优点:键的类型多样,性能稳定,提供丰富的内置方法,遍历顺序严格按插入顺序。
    • 缺点:语法稍复杂,不如 Object 使用广泛。

7. 选择建议

  • 使用 Object
    • 当键为字符串或符号,且不需要频繁添加和删除键值对时。
    • 当需要利用对象的原型链或方法时。
  • 使用 Map
    • 当键的类型多样,特别是包含对象、数组等时。
    • 当需要高效地添加和删除键值对时。
    • 当需要严格按插入顺序遍历时。
      理解 MapObject 的这些区别可以帮助你根据具体需求选择合适的数据结构。

Map 和 Object 在实际应用中的区别

1. 存储用户信息

假设我们需要存储用户信息,每个用户有一个唯一的 ID 和一些其他信息,如姓名和年龄。

使用 Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let users = {};
// 添加用户
users[1] = { name: "Alice", age: 30 };
users[2] = { name: "Bob", age: 25 };
users[3] = { name: "Charlie", age: 35 };
// 获取用户信息
console.log(users[1]); // { name: "Alice", age: 30 }
// 删除用户
delete users[2];
// 遍历用户
for (let id in users) {
console.log(id, users[id]);
}
// 输出:
// 1 { name: "Alice", age: 30 }
// 3 { name: "Charlie", age: 35 }

使用 Map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let users = new Map();
// 添加用户
users.set(1, { name: "Alice", age: 30 });
users.set(2, { name: "Bob", age: 25 });
users.set(3, { name: "Charlie", age: 35 });
// 获取用户信息
console.log(users.get(1)); // { name: "Alice", age: 30 }
// 删除用户
users.delete(2);
// 遍历用户
for (let [id, user] of users) {
console.log(id, user);
}
// 输出:
// 1 { name: "Alice", age: 30 }
// 3 { name: "Charlie", age: 35 }

2. 处理唯一值集合

假设我们需要处理一个数组,去除其中的重复值。

使用 Object

1
2
3
4
5
6
7
let array = [1, 2, 2, 3, 4, 4, 5];
let unique = {};
for (let item of array) {
unique[item] = true;
}
let uniqueArray = Object.keys(unique).map(key => parseInt(key));
console.log(uniqueArray); // [1, 2, 3, 4, 5]

使用 Set

1
2
3
4
let array = [1, 2, 2, 3, 4, 4, 5];
let uniqueSet = new Set(array);
let uniqueArray = Array.from(uniqueSet);
console.log(uniqueArray); // [1, 2, 3, 4, 5]

3. 存储复杂键值对

假设我们需要存储一些复杂对象作为键,例如存储用户对象和他们的权限。

使用 Object

1
2
3
4
5
6
7
let userPermissions = {};
let user1 = { id: 1, name: "Alice" };
let user2 = { id: 2, name: "Bob" };
// 由于对象键会被转换为字符串,这里会出问题
userPermissions[user1] = ["read", "write"];
userPermissions[user2] = ["read"];
console.log(userPermissions); // { '[object Object]': ['read'] }

使用 Map

1
2
3
4
5
6
7
let userPermissions = new Map();
let user1 = { id: 1, name: "Alice" };
let user2 = { id: 2, name: "Bob" };
userPermissions.set(user1, ["read", "write"]);
userPermissions.set(user2, ["read"]);
console.log(userPermissions.get(user1)); // ['read', 'write']
console.log(userPermissions.get(user2)); // ['read']

4. 频繁添加和删除键值对

假设我们需要频繁地添加和删除键值对,例如在一个缓存系统中。

使用 Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let cache = {};
// 添加缓存
cache["key1"] = "value1";
cache["key2"] = "value2";
// 删除缓存
delete cache["key1"];
// 检查缓存是否存在
console.log(cache.hasOwnProperty("key1")); // false
// 遍历缓存
for (let key in cache) {
console.log(key, cache[key]);
}
// 输出:
// key2 value2

使用 Map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let cache = new Map();
// 添加缓存
cache.set("key1", "value1");
cache.set("key2", "value2");
// 删除缓存
cache.delete("key1");
// 检查缓存是否存在
console.log(cache.has("key1")); // false
// 遍历缓存
for (let [key, value] of cache) {
console.log(key, value);
}
// 输出:
// key2 value2

总结

  • Object
    • 优点:语法简洁,广泛使用,适合存储键为字符串或符号的简单数据结构。
    • 缺点:键的类型有限,性能在频繁操作时可能下降,方法返回的是数组,需要额外处理。
  • Map
    • 优点:键的类型多样,性能稳定,提供丰富的内置方法,遍历顺序严格按插入顺序,适合存储复杂键值对和频繁操作的场景。
    • 缺点:语法稍复杂,不如 Object 使用广泛。
      根据具体需求选择合适的数据结构可以提高代码的可读性和性能。

Set

Set 对象是 JavaScript 中的一种集合数据结构,用于存储唯一的值。Set 中的每个值都只能出现一次,这使得它在处理唯一性数据时非常有用。以下是对 Set 对象的详细解释:

创建 Set 对象

1
let mySet = new Set();

常用方法

1. add(value)

  • 用途:向 Set 中添加一个新的值。如果值已经存在,则不会重复添加。
  • 示例
    1
    2
    3
    mySet.add(1);
    mySet.add(2);
    mySet.add(1); // 1 已经存在,不会重复添加

2. has(value)

  • 用途:检查 Set 中是否存在指定的值。返回 truefalse
  • 示例
    1
    2
    console.log(mySet.has(1)); // true
    console.log(mySet.has(3)); // false

3. delete(value)

  • 用途:从 Set 中删除指定的值。返回 true 表示删除成功,false 表示值不存在。
  • 示例
    1
    2
    console.log(mySet.delete(1)); // true
    console.log(mySet.has(1)); // false

4. clear()

  • 用途:清空 Set 中的所有值。
  • 示例
    1
    2
    mySet.clear();
    console.log(mySet.size); // 0

5. size

  • 用途:返回 Set 中值的数量。
  • 示例
    1
    2
    3
    mySet.add(1);
    mySet.add(2);
    console.log(mySet.size); // 2

遍历 Set 对象

Set 对象提供了多种遍历方法,包括 for...of 循环、forEach 方法等。

1. for...of 循环

  • 用途:遍历 Set 中的每个值。
  • 示例
    1
    2
    3
    4
    5
    6
    for (let value of mySet) {
    console.log(value);
    }
    // 输出:
    // 1
    // 2

2. forEach 方法

  • 用途:遍历 Set 中的每个值,并对每个值执行指定的函数。
  • 示例
    1
    2
    3
    4
    5
    6
    mySet.forEach((value) => {
    console.log(value);
    });
    // 输出:
    // 1
    // 2

示例:使用 Set 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let mySet = new Set();
// 添加值
mySet.add(1);
mySet.add(2);
mySet.add(3);
mySet.add(1); // 1 已经存在,不会重复添加
// 获取值的数量
console.log(mySet.size); // 3
// 检查值是否存在
console.log(mySet.has(2)); // true
console.log(mySet.has(4)); // false
// 删除值
mySet.delete(2);
console.log(mySet.has(2)); // false
// 遍历 Set
for (let value of mySet) {
console.log(value);
}
// 输出:
// 1
// 3
// 清空 Set
mySet.clear();
console.log(mySet.size); // 0

总结

Set 对象是 JavaScript 中处理唯一值集合的强大工具,提供了丰富的方法来操作和遍历值。与数组相比,Set 自动处理值的唯一性,使得它在处理去重数据时更加高效和方便。以下是一些常见的使用场景:

  • 去重:从数组中去除重复值。
    1
    2
    3
    let array = [1, 2, 2, 3, 4, 4, 5];
    let uniqueArray = Array.from(new Set(array));
    console.log(uniqueArray); // [1, 2, 3, 4, 5]
  • 集合操作:进行集合的并集、交集、差集等操作。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let set1 = new Set([1, 2, 3]);
    let set2 = new Set([2, 3, 4]);
    // 并集
    let union = new Set([...set1, ...set2]);
    console.log(union); // Set(4) {1, 2, 3, 4}
    // 交集
    let intersection = new Set([...set1].filter(x => set2.has(x)));
    console.log(intersection); // Set(2) {2, 3}
    // 差集
    let difference = new Set([...set1].filter(x => !set2.has(x)));
    console.log(difference); // Set(1) {1}
    理解 Set 对象的这些特性和方法可以帮助你更高效地处理唯一值集合。

set和map区别

JavaScript中的SetMap都是ES6新增的数据结构,它们在功能和用途上有一些区别,以下是详细的对比:
一、存储结构方面

  1. Set
    • Set是一个简单的键的集合,其中的元素是唯一的,没有键值对的概念。它的存储结构类似于一个数组,但不允许重复的元素存在。例如:
      1
      2
      3
      4
      5
      let mySet = new Set();
      mySet.add(1);
      mySet.add(2);
      mySet.add(2); // 重复的元素不会被添加
      console.log(mySet); // Set(2) {1, 2}
      Set中,每个元素都是唯一的,它通过内部机制来保证元素的唯一性。
  2. Map
    • Map是一个键值对的集合,类似于对象(Object),但是它的键可以是任何类型,包括对象、函数等。例如:
      1
      2
      3
      4
      5
      let myMap = new Map();
      myMap.set('key1', 'value1');
      myMap.set(2, 'value2');
      myMap.set({objKey: 'objValue'}, 'value3');
      console.log(myMap); // Map(3) {'key1' => 'value1', 2 => 'value2', {objKey: 'objValue'} => 'value3'}
      Map中,每个键都有一个对应的值,而且键可以是任意类型,这使得Map在处理复杂数据结构时更加灵活。

二、操作方法方面

  1. Set
    • 常用方法有add()(添加元素)、delete()(删除元素)、has()(判断元素是否存在)、clear()(清空所有元素)等。例如:
      1
      2
      3
      4
      5
      let mySet = new Set();
      mySet.add('apple');
      console.log(mySet.has('apple')); // true
      mySet.delete('apple');
      console.log(mySet.has('apple')); // false
      这些方法主要围绕元素的增删查改进行操作,比较简单直接。
  2. Map
    • 常用方法有set()(设置键值对)、get()(获取键对应的值)、has()(判断键是否存在)、delete()(删除键值对)、clear()(清空所有键值对)等。例如:
      1
      2
      3
      4
      5
      6
      let myMap = new Map();
      myMap.set('fruit', 'apple');
      console.log(myMap.get('fruit')); // 'apple'
      console.log(myMap.has('fruit')); // true
      myMap.delete('fruit');
      console.log(myMap.has('fruit')); // false
      Map的方法主要围绕键值对进行操作,功能更加丰富,可以方便地处理键和值之间的关系。

三、遍历方面

  1. Set
    • 可以使用for...of循环进行遍历,遍历的顺序是元素插入的顺序。例如:
      1
      2
      3
      4
      5
      let mySet = new Set(['apple', 'banana', 'orange']);
      for (let item of mySet) {
      console.log(item);
      }
      // 输出:apple banana orange
      也可以使用forEach()方法遍历:
      1
      2
      3
      mySet.forEach((item) => {
      console.log(item);
      });
  2. Map
    • 同样可以使用for...of循环遍历,遍历的顺序也是键值对插入的顺序,每次遍历得到的是一个数组,数组的第一个元素是键,第二个元素是值。例如:
      1
      2
      3
      4
      5
      let myMap = new Map([['fruit', 'apple'], ['color', 'red']]);
      for (let [key, value] of myMap) {
      console.log(key + ': ' + value);
      }
      // 输出:fruit: apple color: red
      也可以使用forEach()方法遍历:
      1
      2
      3
      myMap.forEach((value, key) => {
      console.log(key + ': ' + value);
      });
      此外,Map还可以通过keys()(获取所有键的迭代器)、values()(获取所有值的迭代器)、entries()(获取所有键值对的迭代器)等方法进行更灵活的遍历。

四、用途方面

  1. Set
    • 适用于需要存储唯一元素的场景。例如,去除数组中的重复项:
      1
      2
      3
      let array = [1, 2, 2, 3, 4, 4, 5];
      let uniqueArray = [...new Set(array)];
      console.log(uniqueArray); // [1, 2, 3, 4, 5]
    • 也可以用于判断元素是否存在于集合中,进行一些集合运算(如并集、交集等)。
  2. Map
    • 适用于需要存储键值对,并且键可以是任意类型的情况。例如,存储用户信息,其中键是用户对象,值是用户的一些额外信息:
      1
      2
      3
      4
      5
      6
      let user1 = {name: 'Alice'};
      let user2 = {name: 'Bob'};
      let userInfoMap = new Map();
      userInfoMap.set(user1, {age: 25, gender: 'female'});
      userInfoMap.set(user2, {age: 30, gender: 'male'});
      console.log(userInfoMap.get(user1)); // {age: 25, gender: 'female'}
    • 在处理复杂的数据结构和需要频繁访问键值对应关系的场景中非常有用。

WeakSet 和 WeakMap

1. WeakSet

定义: WeakSet 是一个集合,其中的元素都是对象,并且这些对象是弱引用的。这意味着如果这些对象没有其他强引用,垃圾回收机制可以自动回收这些对象,WeakSet 中的对应项也会自动被移除。
主要方法:

  • add(value):向 WeakSet 中添加一个对象。
  • delete(value):从 WeakSet 中删除一个对象。
  • has(value):检查 WeakSet 中是否存在某个对象。
    应用场景:
  1. 避免内存泄漏:存储一组 DOM 节点,当这些节点被移除后,可以自动释放内存。
    1
    2
    3
    4
    5
    6
    const domNodes = new WeakSet();
    const button = document.createElement('button');
    button.textContent = 'Click me';
    document.body.appendChild(button);
    domNodes.add(button);
    document.body.removeChild(button);
  2. 跟踪动态数据:跟踪频繁修改的对象,而不会因为长时间保持引用而导致内存泄漏。
    1
    2
    3
    4
    5
    6
    7
    8
    const dataPoints = new WeakSet();
    function addDataPoint(point) {
    dataPoints.add(point);
    }
    const point1 = { x: 10, y: 20 };
    const point2 = { x: 30, y: 40 };
    addDataPoint(point1);
    addDataPoint(point2);
  3. 存储临时对象:存储临时对象,当这些对象不再需要时,可以自动释放内存。
    1
    2
    3
    4
    5
    6
    7
    const myWeakSet = new WeakSet();
    function createTemporaryObject(id) {
    const obj = { id: id, data: '/* ... 一些临时数据 ... */' };
    myWeakSet.add(obj);
    }
    createTemporaryObject(1);
    createTemporaryObject(2);

2. WeakMap

定义: WeakMap 是一个键值对的集合,其中的键必须是对象,值可以是任意类型。键是弱引用的,这意味着如果这些键对象没有其他强引用,垃圾回收机制可以自动回收这些对象,WeakMap 中的对应键值对也会自动被移除。
主要方法:

  • set(key, value):向 WeakMap 中添加或更新一个键值对。
  • get(key):获取对应键的值。
  • delete(key):从 WeakMap 中删除一个键值对。
  • has(key):检查 WeakMap 中是否存在某个键。
    应用场景:
  1. 缓存:缓存计算结果或频繁访问的数据,以提高性能。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    const cache = new WeakMap();
    function calculation(obj) {
    if (cache.has(obj)) {
    return cache.get(obj);
    }
    const result = Math.random();
    cache.set(obj, result);
    return result;
    }
    const keyObj = {};
    console.log(calculation(keyObj));
    console.log(calculation(keyObj));
    keyObj = null;
  2. 避免内存泄漏:存储监听事件的引用,当监听事件被移除后,可以自动释放内存。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const btn1 = document.querySelector("#btn1");
    const btn2 = document.querySelector("#btn2");
    const btnMap = new WeakMap();
    function handleBtn1Click() {}
    function handleBtn2Click() {}
    btnMap.set(btn1, handleBtn1Click);
    btnMap.set(btn2, handleBtn2Click);
    btn1.addEventListener("click", btnMap.get(btn1));
    btn2.addEventListener("click", btnMap.get(btn2));
    btn1.remove();
    btn2.remove();
  3. 存储私有属性:保护私有属性不被外部访问。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    const privateProps = new WeakMap();
    class Person {
    constructor(name) {
    this.name = name;
    privateProps.set(this, { age: 0 });
    this.incrementAge = () => {
    const props = privateProps.get(this);
    props.age++;
    };
    this.getAge = () => {
    const props = privateProps.get(this);
    return props.age;
    };
    }
    }
    const alice = new Person('Alice');
    console.log(alice.name); // Alice
    console.log(alice.getAge()); // 0
    alice.incrementAge();
    console.log(alice.getAge()); // 1
    console.log(alice.age); // undefined
  4. 优化深拷贝:解决循环引用问题。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    function deepCopy(obj, visited = new WeakMap()) {
    if (obj === null || typeof obj !== 'object') {
    return obj;
    }
    if (visited.has(obj)) {
    return visited.get(obj);
    }
    if (obj instanceof Array) {
    let copy = [];
    visited.set(obj, copy);
    for (let i = 0; i < obj.length; i++) {
    copy[i] = deepCopy(obj[i], visited);
    }
    return copy;
    }
    if (obj instanceof Object) {
    let copy = {};
    visited.set(obj, copy);
    for (let key in obj) {
    copy[key] = deepCopy(obj[key], visited);
    }
    return copy;
    }
    return obj;
    }

总结

  • WeakSet:适用于需要存储一组对象引用的场景,这些对象在没有其他强引用时可以被自动回收。常用于避免内存泄漏、跟踪动态数据和存储临时对象。
  • WeakMap:适用于需要存储键值对的场景,键是对象,值可以是任意类型。键是弱引用的,当键对象没有其他强引用时,可以被自动回收。常用于缓存、避免内存泄漏、存储私有属性和优化深拷贝。

特点对比

特点 WeakSet WeakMap
键类型 只能是对象 键必须是对象,值可以是任意类型
引用类型 弱引用 弱引用
可迭代 不可迭代 不可迭代
常见方法 add, delete, has set, get, delete, has
应用场景 避免内存泄漏、跟踪动态数据、存储临时对象 缓存、避免内存泄漏、存储私有属性、优化深拷贝
希望这些信息对你有所帮助!