Set

Set数据结构是ES6新增的,其行为「类似数组」,和数组不同的是**Set**的数据都是唯一的。

Set是一个构造函数,所以创建Set对象需要进行实例化:

  1. const set = new Set();

image.png

Set构造函数可以在实例化的时候传递参数进行初始化数据: :::danger ⚠️ 注意
Set构造函数的参数必须是一个具有iterator迭代器接口的数据结构,这也意味着传递object数据则不行。 :::

  1. let set = new Set(["10", "20", "30", "10"]);
  2. console.log(set); // {10, 20, 30} ,Set 的数据都是唯一的,所以能够进行去重
  3. console.log(set.size); // size 表示 Set 数据的大小,类似数组的 length 长度属性
  4. new Set({}); // object is not iterable (cannot read property Symbol(Symbol.iterator))

image.png

Set传递特殊值:

  1. var set = new Set([undefined, undefined, null, null, "10", 10, NaN, NaN, {}, {}]);
  2. console.log(set); // Set{undefined, null, '10', 10, NaN, {}, {}}

可以看到上面的代码针对特殊值也能进行去重,但是保留下两个{},这是因为{}{}的堆内存地址不同!

  1. let obj = {name: "zhangsan"}
  2. var set = new Set([obj, obj]);
  3. console.log(set); // Set{ {name: "zhangsan"} }

方法

image.png
Set构造函数的原型方法可以分为两类:操作Set本身、Set遍历

1、操作Set对象本身 :::info set.add()Set对象新增数据,返回Set对象本身,意味着可以链式操作add方法
set.delete()Set对象删除数据,返回是否删除成功的布尔值
set.clear()Set对象的数据全部清除,返回undefind
set.has()判断Set对象是否存在某个值,返回布尔值 :::

  1. let x = { id: 1 },
  2. y = { id: 2 };
  3. let set = new Set();
  4. let res = set.add(x);
  5. console.log(res); // Set{ {id: 1} }
  6. set.add(y).add("foo");
  7. console.log(set); // Set{ {id: 1}, { id: 2 }, "foo" }
  8. let res2 = set.delete(x);
  9. console.log(res2); // true
  10. console.log(set); // Set{ { id: 2 }, "foo" }
  11. let res3 = set.clear();
  12. console.log(res3); // undefined
  13. console.log(set); // Set{}
  14. let res4 = set.has(y);
  15. console.log(res4); // false

需要注意的是,既然打印Set后再清空Set对象,结果已经被清空(虽然控制台还是能显示数据):

  1. let set = new Set([10, 20, 30, "a", "b", "c"]);
  2. console.log(set);
  3. set.clear();

image.png

2、遍历Set对象 :::info 这些方法和数组的方法都类似。

set.keys()遍历Set对象的key,返回迭代器对象
set.values()遍历Set对象的value,返回迭代器对象
set.entries()遍历Set对象的keyvalue,返回迭代器对象
set.forEach()遍历Set对象

⚠️ 注意
Set对象没有索引值,虽然浏览器的控制台能看到。 ::: image.png

  1. let set = new Set([10, 20, 30, "a", "b", "c"]);
  2. console.log(set.keys()); // SetIterator{ 10, 20, 30, "a", "b", "c" }
  3. console.log(set.values()); // SetIterator{ 10, 20, 30, "a", "b", "c" }
  4. console.log(set.entries()); // SetIterator{ 10=>10, 20=>20, 30=>30, "a"=>"a", "b"=>"b", "c"=>"c" }
  5. set.forEach((el,index,arr)=>{
  6. console.log(el, index, arr)
  7. })
  8. // 10 10 Set(6) {10, 20, 30, 'a', 'b', …}
  9. // 20 20 Set(6) {10, 20, 30, 'a', 'b', …}
  10. // 30 30 Set(6) {10, 20, 30, 'a', 'b', …}
  11. // "a" "a" Set(6) {10, 20, 30, 'a', 'b', …}
  12. // "b" "b" Set(6) {10, 20, 30, 'a', 'b', …}
  13. // "c" "c" Set(6) {10, 20, 30, 'a', 'b', …}

既然返回的迭代器对象,所以可以用for...of...进行迭代:

  1. for (const iterator of set.keys()) {
  2. console.log(iterator); // 10 20 30 "a" "b" "c"
  3. }
  4. for (const iterator of set.values()) {
  5. console.log(iterator); // 10 20 30 "a" "b" "c"
  6. }
  7. for (const iterator of set.entries()) {
  8. console.log(iterator); // [10, 10] [20, 20] [30, 30] ["a", "a"] ["b", "b"] ["c", "c"]
  9. }
  10. for (const iterator of set) {
  11. console.log(iterator); // // 10 20 30 "a" "b" "c"
  12. }

🥁 实例:
我们利用Set的数据唯一性可以快速的实现数组去重。

  1. let array = [10, 20, 30, 10, 20, "c", 10, "c"];
  2. let set = new Set(array);
  3. console.log([...set]); // [10, 20, 30, 'c']
  4. console.log(Array.from(set)); // [10, 20, 30, 'c']

以上代码转换为Set数据后可以利用拓展运算符将具有iterator接口的数据类型转换为数组或者利用ES6新增方法fromSet数据转换为数组。

Map

Map也是ES6新增的数据结构,其行为类似Object,和Object不同的是MapKey可以是任意数据类型。

对象的Key只能是字符串:

  1. let x = { id: 1 },
  2. y = { id: 2 };
  3. let m = {};
  4. m[x] = "foo";
  5. m[y] = "bar";
  6. console.log(m); // {[object Object]: 'bar'}

Map是一个构造函数,所以需要进行实例化:

  1. let map = new Map();
  2. let x = { id: 1 },
  3. y = { id: 2 };
  4. map.set(x, "foo");
  5. map.set(y, "bar");
  6. console.log(map.get(x))
  7. console.log(map);

image.png

MapSet对象一样,也可以在实例化的时候传递参数进行数据初始化: :::danger ⚠️ 注意
Map构造函数的参数必须是一个具有iterator迭代器接口的数据结构,而且必须是多维,这也意味着传递object数据则不行。 :::

  1. let map = new Map([
  2. ["name", "张三"],
  3. ["age", "20"],
  4. ]);
  5. map.set("hobby", "chfian");
  6. console.log(map);

image.png

同名的key将会被覆盖:

  1. const map = new Map();
  2. map.set(1, "foo");
  3. map.set(1, "bar"); // 同名key会进行覆盖
  4. map.set([5], "bar"); // 同名key会进行覆盖
  5. console.log(map); // Map(2) {1 => 'bar', Array(1) => 'bar'}
  6. console.log(map.get(1)); // bar
  7. console.log(map.get([5])); // undefined;[5] 和 [5] 是两个不同的堆内存地址

Map传递其他数据类型:

  1. const map = new Map();
  2. map.set(-0, "123");
  3. map.set(true, "456");
  4. map.set("true", "678");
  5. map.set(undefined, "10");
  6. map.set(null, 2);
  7. map.set(NaN, 20);
  8. console.log(map.get(+0)); // 123
  9. console.log(map.get(true)); // 456
  10. console.log(map.get(undefined)); // 10
  11. console.log(map.get(null)); // 2
  12. console.log(map.get(NaN)); // 20
  13. console.log(map.size); // 6

方法

image.png
Map构造函数的原型方法可以分为两类:操作Map本身、Map遍历

1、操作Map对象本身 :::info map.set()Map对象新增数据,返回Map对象本身,意味着可以链式操作set方法
map.get()获取Map对象的数据,返回获取到的数据
map.delete()Map对象删除数据,返回是否删除成功的布尔值
map.clear()Map对象的数据全部清除,返回undefind
map.has()判断Map对象是否存在某个值,返回布尔值

Set对象多了setget方法,少了add方法。 :::

  1. var x = { id: 1 };
  2. var y = { id: 2 };
  3. const map = new Map();
  4. console.log(map.size); // 0
  5. map.set(x, "foo").set(y, "bar");
  6. console.log(map); // Map(2) {{…} => 'foo', {…} => 'bar'}
  7. map.delete(x);
  8. console.log(map); // Map(1) {{…} => 'bar'}
  9. console.log(map.has(x)); // false
  10. map.clear();
  11. console.log(map); // Map(0) {size: 0}

需要注意的是,既然打印Map后再清空Map对象,结果已经被清空(虽然控制台还是能显示数据):

  1. const map = new Map([
  2. ["name", "123"],
  3. ["age", "20"],
  4. ]);
  5. console.log(map);
  6. map.delete("name");

image.png

2、遍历Map对象 :::info 这些方法和数组的方法都类似。

map.keys()遍历Map对象的key,返回迭代器对象
map.values()遍历Map对象的value,返回迭代器对象
map.entries()遍历Map对象的keyvalue,返回迭代器对象
map.forEach()遍历Map对象 :::

  1. const map = new Map([
  2. ["name", "123"],
  3. ["age", "20"],
  4. ]);
  5. console.log(map.keys()); // MapIterator {'name', 'age'}
  6. console.log(map.values()); // MapIterator {'123', '20'}
  7. console.log(map.entries()); // MapIterator {'name' => '123', 'age' => '20'}
  8. map.forEach((el, index, arr) => {
  9. console.log(el, index, arr);
  10. });
  11. // 123 name Map(2) {'name' => '123', 'age' => '20'}
  12. // 20 age Map(2) {'name' => '123', 'age' => '20'}

既然返回的迭代器对象,所以可以用for...of...进行迭代:

  1. const map = new Map([
  2. ["name", "123"],
  3. ["age", "20"],
  4. ]);
  5. for (const iterator of map.keys()) {
  6. console.log(iterator); // name age
  7. }
  8. for (const iterator of map.values()) {
  9. console.log(iterator); // 123 20
  10. }
  11. for (const iterator of map.entries()) {
  12. console.log(iterator); // ['name', '123'] ['age', '20']
  13. }
  14. for (const iterator of map) {
  15. console.log(iterator); // ['name', '123'] ['age', '20']
  16. }
  17. for (const [key, value] of map) {
  18. console.log(key + "=" + value); // name=123 age=20
  19. }

WeakMap 和 WeakSet

WeakMapWeakSetMapSet对象用法基本上一致,简单理解就是削弱版的MapSet

  1. const wm = new WeakMap();
  2. const ws = new WeakSet();

区别:
1、WeakMapWeakSet没有遍历方法
image.png
image.png

2、WeakMapWeakSet的数据成员只能是对象

  1. let ws = new WeakSet();
  2. ws.add({ name: 1 }); // 正常
  3. ws.add(1); // Invalid value used in weak set
  1. let wm = new WeakMap();
  2. wm.set({ name: 1 }, "111");
  3. wm.set(1, 1); // Invalid value used as weak map key

3、WeakMapWeakSet是弱引用,引用会直接垃圾回收(所以没有遍历方法,因为遍历的时候已经被垃圾回收了。)