Set
Set数据结构是ES6新增的,其行为「类似数组」,和数组不同的是**Set**的数据都是唯一的。
Set是一个构造函数,所以创建Set对象需要进行实例化:
const set = new Set();

Set构造函数可以在实例化的时候传递参数进行初始化数据:
:::danger
⚠️ 注意Set构造函数的参数必须是一个具有iterator迭代器接口的数据结构,这也意味着传递object数据则不行。
:::
let set = new Set(["10", "20", "30", "10"]);console.log(set); // {10, 20, 30} ,Set 的数据都是唯一的,所以能够进行去重console.log(set.size); // size 表示 Set 数据的大小,类似数组的 length 长度属性new Set({}); // object is not iterable (cannot read property Symbol(Symbol.iterator))

Set传递特殊值:
var set = new Set([undefined, undefined, null, null, "10", 10, NaN, NaN, {}, {}]);console.log(set); // Set{undefined, null, '10', 10, NaN, {}, {}}
可以看到上面的代码针对特殊值也能进行去重,但是保留下两个{},这是因为{}和{}的堆内存地址不同!
let obj = {name: "zhangsan"}var set = new Set([obj, obj]);console.log(set); // Set{ {name: "zhangsan"} }
方法

Set构造函数的原型方法可以分为两类:操作Set本身、Set遍历
1、操作Set对象本身
:::info
set.add()给Set对象新增数据,返回Set对象本身,意味着可以链式操作add方法set.delete()给Set对象删除数据,返回是否删除成功的布尔值set.clear()将Set对象的数据全部清除,返回undefindset.has()判断Set对象是否存在某个值,返回布尔值
:::
let x = { id: 1 },y = { id: 2 };let set = new Set();let res = set.add(x);console.log(res); // Set{ {id: 1} }set.add(y).add("foo");console.log(set); // Set{ {id: 1}, { id: 2 }, "foo" }let res2 = set.delete(x);console.log(res2); // trueconsole.log(set); // Set{ { id: 2 }, "foo" }let res3 = set.clear();console.log(res3); // undefinedconsole.log(set); // Set{}let res4 = set.has(y);console.log(res4); // false
需要注意的是,既然打印Set后再清空Set对象,结果已经被清空(虽然控制台还是能显示数据):
let set = new Set([10, 20, 30, "a", "b", "c"]);console.log(set);set.clear();

2、遍历Set对象
:::info
这些方法和数组的方法都类似。
set.keys()遍历Set对象的key,返回迭代器对象set.values()遍历Set对象的value,返回迭代器对象set.entries()遍历Set对象的key和value,返回迭代器对象set.forEach()遍历Set对象
⚠️ 注意Set对象没有索引值,虽然浏览器的控制台能看到。
:::

let set = new Set([10, 20, 30, "a", "b", "c"]);console.log(set.keys()); // SetIterator{ 10, 20, 30, "a", "b", "c" }console.log(set.values()); // SetIterator{ 10, 20, 30, "a", "b", "c" }console.log(set.entries()); // SetIterator{ 10=>10, 20=>20, 30=>30, "a"=>"a", "b"=>"b", "c"=>"c" }set.forEach((el,index,arr)=>{console.log(el, index, arr)})// 10 10 Set(6) {10, 20, 30, 'a', 'b', …}// 20 20 Set(6) {10, 20, 30, 'a', 'b', …}// 30 30 Set(6) {10, 20, 30, 'a', 'b', …}// "a" "a" Set(6) {10, 20, 30, 'a', 'b', …}// "b" "b" Set(6) {10, 20, 30, 'a', 'b', …}// "c" "c" Set(6) {10, 20, 30, 'a', 'b', …}
既然返回的迭代器对象,所以可以用for...of...进行迭代:
for (const iterator of set.keys()) {console.log(iterator); // 10 20 30 "a" "b" "c"}for (const iterator of set.values()) {console.log(iterator); // 10 20 30 "a" "b" "c"}for (const iterator of set.entries()) {console.log(iterator); // [10, 10] [20, 20] [30, 30] ["a", "a"] ["b", "b"] ["c", "c"]}for (const iterator of set) {console.log(iterator); // // 10 20 30 "a" "b" "c"}
🥁 实例:
我们利用Set的数据唯一性可以快速的实现数组去重。
let array = [10, 20, 30, 10, 20, "c", 10, "c"];let set = new Set(array);console.log([...set]); // [10, 20, 30, 'c']console.log(Array.from(set)); // [10, 20, 30, 'c']
以上代码转换为Set数据后可以利用拓展运算符将具有iterator接口的数据类型转换为数组或者利用ES6新增方法from将Set数据转换为数组。
Map
Map也是ES6新增的数据结构,其行为类似Object,和Object不同的是Map的Key可以是任意数据类型。
对象的Key只能是字符串:
let x = { id: 1 },y = { id: 2 };let m = {};m[x] = "foo";m[y] = "bar";console.log(m); // {[object Object]: 'bar'}
Map是一个构造函数,所以需要进行实例化:
let map = new Map();let x = { id: 1 },y = { id: 2 };map.set(x, "foo");map.set(y, "bar");console.log(map.get(x))console.log(map);

Map和Set对象一样,也可以在实例化的时候传递参数进行数据初始化:
:::danger
⚠️ 注意Map构造函数的参数必须是一个具有iterator迭代器接口的数据结构,而且必须是多维,这也意味着传递object数据则不行。
:::
let map = new Map([["name", "张三"],["age", "20"],]);map.set("hobby", "chfian");console.log(map);

同名的key将会被覆盖:
const map = new Map();map.set(1, "foo");map.set(1, "bar"); // 同名key会进行覆盖map.set([5], "bar"); // 同名key会进行覆盖console.log(map); // Map(2) {1 => 'bar', Array(1) => 'bar'}console.log(map.get(1)); // barconsole.log(map.get([5])); // undefined;[5] 和 [5] 是两个不同的堆内存地址
Map传递其他数据类型:
const map = new Map();map.set(-0, "123");map.set(true, "456");map.set("true", "678");map.set(undefined, "10");map.set(null, 2);map.set(NaN, 20);console.log(map.get(+0)); // 123console.log(map.get(true)); // 456console.log(map.get(undefined)); // 10console.log(map.get(null)); // 2console.log(map.get(NaN)); // 20console.log(map.size); // 6
方法

Map构造函数的原型方法可以分为两类:操作Map本身、Map遍历
1、操作Map对象本身
:::info
map.set()给Map对象新增数据,返回Map对象本身,意味着可以链式操作set方法map.get()获取Map对象的数据,返回获取到的数据map.delete()给Map对象删除数据,返回是否删除成功的布尔值map.clear()将Map对象的数据全部清除,返回undefindmap.has()判断Map对象是否存在某个值,返回布尔值
比Set对象多了set、get方法,少了add方法。
:::
var x = { id: 1 };var y = { id: 2 };const map = new Map();console.log(map.size); // 0map.set(x, "foo").set(y, "bar");console.log(map); // Map(2) {{…} => 'foo', {…} => 'bar'}map.delete(x);console.log(map); // Map(1) {{…} => 'bar'}console.log(map.has(x)); // falsemap.clear();console.log(map); // Map(0) {size: 0}
需要注意的是,既然打印Map后再清空Map对象,结果已经被清空(虽然控制台还是能显示数据):
const map = new Map([["name", "123"],["age", "20"],]);console.log(map);map.delete("name");

2、遍历Map对象
:::info
这些方法和数组的方法都类似。
map.keys()遍历Map对象的key,返回迭代器对象map.values()遍历Map对象的value,返回迭代器对象map.entries()遍历Map对象的key和value,返回迭代器对象map.forEach()遍历Map对象
:::
const map = new Map([["name", "123"],["age", "20"],]);console.log(map.keys()); // MapIterator {'name', 'age'}console.log(map.values()); // MapIterator {'123', '20'}console.log(map.entries()); // MapIterator {'name' => '123', 'age' => '20'}map.forEach((el, index, arr) => {console.log(el, index, arr);});// 123 name Map(2) {'name' => '123', 'age' => '20'}// 20 age Map(2) {'name' => '123', 'age' => '20'}
既然返回的迭代器对象,所以可以用for...of...进行迭代:
const map = new Map([["name", "123"],["age", "20"],]);for (const iterator of map.keys()) {console.log(iterator); // name age}for (const iterator of map.values()) {console.log(iterator); // 123 20}for (const iterator of map.entries()) {console.log(iterator); // ['name', '123'] ['age', '20']}for (const iterator of map) {console.log(iterator); // ['name', '123'] ['age', '20']}for (const [key, value] of map) {console.log(key + "=" + value); // name=123 age=20}
WeakMap 和 WeakSet
WeakMap和WeakSet与Map和Set对象用法基本上一致,简单理解就是削弱版的Map和Set
const wm = new WeakMap();const ws = new WeakSet();
区别:
1、WeakMap和WeakSet没有遍历方法

2、WeakMap和WeakSet的数据成员只能是对象
let ws = new WeakSet();ws.add({ name: 1 }); // 正常ws.add(1); // Invalid value used in weak set
let wm = new WeakMap();wm.set({ name: 1 }, "111");wm.set(1, 1); // Invalid value used as weak map key
3、WeakMap和WeakSet是弱引用,引用会直接垃圾回收(所以没有遍历方法,因为遍历的时候已经被垃圾回收了。)
