ES6 Set & Map

最后更新:
阅读次数:
  • 关键字: Set、WeakSet、Map、WeakMap

本文提到了迭代器(Iterator)和可迭代对象(Iterable),这里先不做详细解释,后面会有总结

Set

集合(Set)对象允许你存储任意类型的唯一值(不能重复),无论它是原始值或者是对象引用。

  • 利用 Set() 构造函数来生成 Set 数据结构: new Set([iterable])
let set1 = new Set([1, 1, 2, 3, 1, 12, 3]);
set1; // Set {1, 2, 3, 12}

let set2 = new Set();
set2.add("hello");
set2.add(2);
set2.add(3);
set2.add(2);
set2; // Set {"hello", 2, 3}
  • Set 中不允许有重复的数据(初始化时若传入了重复数据,会自动去重),所以可以用它来去除数组重复成员
let arr = [1, 2, 3, 4, 2, 3, 2, 11, 2];
arr = [...new Set(arr)];
arr; // [1, 2, 3, 4, 11]
  • 将 Set 转为 Array
let set = new Set([1, 2, 3, 4, 1, 2, 3]);
set; // Set {1, 2, 3, 4}

// 1. 使用 Array.from() 方法
let arr1 = Array.from(set);
arr1; // [1, 2, 3, 4]

// 2. 使用扩展运算符 ...
let arr2 = [...set];
arr2; // [1, 2, 3, 4]
  • 查看当前 Set 包含的元素的个数用 size 属性,而不是 length 属性,注意与 Array 区分开
let set = new Set([11, 2, 33]);
console.log(set.size); // 3
console.log(set.length); // undefined

let arr = [11, 2, 33];
console.log(arr.size); // undefined
console.log(arr.length); // 3
  • 基本的操作 Set 的方法

    • Set.prototype.add(value): 向 Set 尾部添加元素,返回添加元素后的该 Set 对象
    • Set.prototype.delete(value): 删除与 value 等值的元素,返回一个布尔值(删除成功返回 true)
    • Set.prototype.has(value): 判断 Set 中是否有对应的 value 元素,有则返回 true
    • Set.prototype.clear(): 清空 Set 的所有元素
var set = new Set();
set
.add(1)
.add(2)
.add("hello");
set; // Set {1, 2, "hello"}
  • 遍历 Set 的方法
    • Set.prototype.keys(): 返回一个包含所有键的迭代器对象
    • Set.prototype.values(): 返回一个包含所有值得迭代器对象(其实在 Set 中,这个方法和上面的方法效果一样)
    • Set.prototype.entries(): 返回一个迭代器对象,这个对象的元素是类似 [value, value] 形式的数组(在 Set 中,value 与 value 相同)
    • Set.prototype.forEach(callbackFn[, thisArg]): 遍历指定的 Set

WeakSet

  • WeakSet 是弱引用版的 Set 集合,用于存储对象的弱引用。

  • WeakSet 与 Set 有以下的区别

    • WeakSet 的元素只能是对象,不能是基本类型的值
    • WeakSet 支持 add()、delete()、has() 方法,但不支持 size 属性、forEach()、keys()、values()等方法
    • Set 保存的是对象的强引用,而 WeakSet 保存的是对象值的弱引用
// Set 对象强引用
let set = new Set();
let obj = { name: "aaa" };

set.add(obj);

console.log(set.has(obj)); // true
console.log(set.size); // 1

obj = null;

console.log(set.has(obj)); // false
console.log(set.size); // 1

console.log([...set][0]); // {name: "aaa"}
// Weak Set 对象弱引用
let weakset = new WeakSet();
let obj = { name: "aaa" };

weakset.add(obj);

console.log(weakset.has(obj)); // true
console.log(weakset.size); // undefined

obj = null;

console.log(weakset.has(obj)); // false
console.log(weakset.size); // undefined

console.log([...weakset][0]); // Uncaught TypeError: weakset is not iterable

Map

Map 对象类似于 Object 对象,都是简单的键/值映射。但是二者最主要的区别的键的类型,Object 对象的键只能是字符串或 Symbol 类型,而 Map 对象的键可以是任意的类型(对象或者原始值)。

总的说,Map 是一种更完善的 Hash 结构。

  • 利用 Map() 构造函数来生成 Map 数据结构: new Map([iterable])
let arr = [1, 2, 3, 1, 2, "hello"];
let map1 = new Map(arr.entries());
map1; // Map {0 => 1, 1 => 2, 2 => 3, 3 => 1, 4 => 2…}

let map2 = new Map();
map2.set("prop1", "hello world");
map2.set([1, 2], [1, 2, 3]);
map2; // Map {"prop1" => "hello world", [1, 2] => [1, 2, 3]}
  • 查看当前 Map 包含的元素的个数用 size 属性

  • 基本操作方法

    • Map.prototype.set(key, value): 添加或更改指定键的值,返回更改后的对象
    • Map.prototype.get(key): 返回指定键的值
    • Map.prototype.has(key): 返回一个布尔值,判断是否包含指定的键
    • Map.prototype.delete(key): 删除指定键的键值对,返回一个布尔值(删除成功返回 true)
    • Map.prototype.clear(): 删除所有的键值对
  • 遍历 Map 的方法

    • Map.prototype.keys(): 返回一个包含所有键的迭代器
    • Map.prototype.values(): 返回一个包含所有值的迭代器
    • Map.prototype.entries(): 返回一个迭代器对象,这个对象的元素是类似 [value, value] 形式的数组(在 Set 中,value 与 value 相同)
    • Map.prototype.forEach(callbackFn[, thisArg]): 遍历 Map
      • callback(value, key, map)
  • Map 与其他数据结构之间的转换

    • Array 转 Map:可使用 Array.prototype.entries()
    • Object 转 Map:可通过遍历赋值实现
var map = new Map();

map.set([111], "Array 111");
map.set("A function", function() {
console.log("function");
});
map.set("Obj", { name: "percy" });

console.log(map);
// Map {[111] => "Array 111", "A function" => function, "Obj" => Object {name: "percy"}}

// Map to Array
var arr = [...map];

// 一维数组转 Map
var arr1 = ["aaa", "22", 333];
var map1 = new Map(arr1.entries());
console.log(map1); // Map {0 => "aaa", 1 => "22", 2 => 333}

// 二维数组转 Map
var arr2 = [[111, "world"], [222, "hello"]];
var map2 = new Map(arr2);
console.log(map2); // Map {111 => "world", 222 => "hello"}

WeakMap

  • WeakMap 是弱引用版的 Map 集合,它只支持对象类型的键名。

  • WeakMap 与 Map 的区别如下

    • WeakMap 集合中的键名必须是一个对象,而键值可以为任意值
    • WeakMap 没有 size 属性,并且也不可枚举
    • WeakMap 集合中的键名保存的是对象的弱引用,而键值保存的对象是强引用

WeakMap 集合最大的用途是保存 Web 页面中的 DOM 元素。例如,一些为 Web 页面打造的 JavaScript 库,会通过自定义的对象保存每一个引用的 DOM 元素。

使用这种方法最困难的是,一旦从 Web 页面中移除保存过的 DOM 元素,如何通过库本身将这些对象从集合中清除;否则,库在 DOM 元素无用后可能依然保持对它们的引用,从而导致内存泄漏,最终程序不再正常运行。如果用 WeakMap 集合来跟踪 DOM 元素,这些库仍然可以通过自定义的对象整合每一个 DOM 元素,而且当 DOM 元素消失时,可以自动销毁集合中的相关对象。

-–《深入理解 ES6》

参考资料