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
对象的数据全部清除,返回undefind
set.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); // true
console.log(set); // Set{ { id: 2 }, "foo" }
let res3 = set.clear();
console.log(res3); // undefined
console.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)); // bar
console.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)); // 123
console.log(map.get(true)); // 456
console.log(map.get(undefined)); // 10
console.log(map.get(null)); // 2
console.log(map.get(NaN)); // 20
console.log(map.size); // 6
方法
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
对象多了set
、get
方法,少了add
方法。
:::
var x = { id: 1 };
var y = { id: 2 };
const map = new Map();
console.log(map.size); // 0
map.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)); // false
map.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
是弱引用,引用会直接垃圾回收(所以没有遍历方法,因为遍历的时候已经被垃圾回收了。)