认识Set和Map
在es5中经常用对象来实现集合set
和映射 map
的数据结构,但是这种方式有一些弊端。比如实现集合时,我们不能用 if(set.count)
判断某个元素是否确切存在。在集合中,属性5和'5'会被当作同一个键,还有不能使用对象作为键,因为会转为[object object]
。所以,es6提供了两种新的数据解构:Set
集合和Map
映射。
Set集合
通过 new Set()
创建一个空的集合,通过 add()
方法往集合添加元素:
let set = new Set();
set.add(5);
set.add('5');
console.log(set); // Set { 5, '5' }
添加进集合的元素会自动去重,并且内部使用 Object.is()
方法来判断两个元素是否相等,但是+0和-0除外,他们在集合中被视为相等:
let set = new Set();
set.add(5);
set.add('5');
set.add(5);
console.log(set.size); // 2
集合可用具有迭代器接口的数据进行初始化,比如用一个数组:
let set = new Set([1, 2, 2, 3, 4, 5, 5]);
console.log(set); // Set { 1, 2, 3, 4, 5 }
另外一些集合的方法:
has(key)
: 判断某个值是否存在delete(key)
: 移除集合某一个元素clear()
: 清除集合的所有元素
let set = new Set();
set.add(5);
set.add('5');
console.log(set.has(5)); // true
set.delete(5);
console.log(set.has(5)); // false
console.log(set.size); // 1
set.clear();
console.log(set.size); // 0
类似数组,集合Set
也有 forEach
方法,第一个参数为循环的函数,第二个参数为绑定这个函数的 this
对象。对于循环函数的参数,第一个和第二个都为集合每个循环元素:
let set = new Set([1, 2]);
set.forEach((value, key, own) => {
console.log(key + ' ' + value);
console.log(own === set);
});
// 1 1
// true
// 2 2
// true
Map映射
用 new Map()
新建一个空的映射,通过 set()
方法添加键值对,get()
方法获取对应键的值:
let map = new Map();
map.set('title', 'ECMA 2016');
map.set('year', 2016);
console.log(map.get('title')); // ECMA 2016
console.log(map.get('year')); // 2016
在 Map
集合中,允许对象作为键:
let map = new Map();
let key1 = {},
key2 = {};
map.set(key1, 4).set(key2, 34);
console.log(map); // Map { {} => 4, {} => 34 }
console.log(map.get(key1)); // 4
可以向 Map
构造函数传一个数组来初始化。数组的子元素是包含键和值两个元素的数组:
let map = new Map([['name', 'wozien'], ['age', 25]]);
console.log(map); // Map { 'name' => 'wozien', 'age' => 25 }
Map
和 Set
一样拥有 has(key)
, clear()
, delete(key)
三个方法,并且拥有 size
属性,表示键值对的个数:
let map = new Map();
map.set('name', 'wozien');
map.set('age', 25);
console.log(map.size); // 2
console.log(map.has('name')); // true
map.delete('name');
console.log(map.has('name')); // false
map.clear();
console.log(map.size); // 0
WeakSet和WeakMap
WeakSet
表示弱引用集合,什么是弱引用,来看一个例子:
let set = new Set();
let obj = {};
set.add(obj);
console.log(set.size); // 1
obj = null;
console.log(set.size); // 1
上面的代码先在集合插入一个对象,然后把这个对象的引用obj设置 null
,清除了对该对象的引用。从集合的元素个数不变可以看出,该对象的内存并没有被回收,也就是说集合set仍然引用着这个对象,也称强引用。
相对的,如果存在一种集合,在外部的引用都不存在时,集合的对象会自动被垃圾回收,该集合就可以称为对该对象的弱引用。WeakSet
的作用就是这样:
let set = new WeakSet();
let obj = {};
set.add(obj);
console.log(set.has(obj)); // true
obj = null;
console.log(set.has(obj)); // false
类似的,WeakMap
叫做弱引用Map,它的键名必须为一个对象,否则会报错:
let set = new WeakMap();
let obj = {};
set.set(obj, 1);
console.log(set.has(obj)); // true
obj = null;
console.log(set.has(obj)); // false
WeakSet
和 WeakMap
不支持 clear()
和 forEach()
方法。因为垃圾回收执行不能预测,所谓两者都没有 size
属性。
let set = new WeakMap();
let obj = {};
set.set(obj, 1);
console.log(set.size); // undefined
应用
利用Set
进行数组去重:
let arr = [1, 2, 2, 3, 4, 4, 5];
arr = [...new Set(arr)];
console.log(arr); // [ 1, 2, 3, 4, 5 ]
利用 WeakMap
记录DOM元素的额外信息,并随着DOM的移除自动清除:
let wm = new WeakMap(), element = document.querySelector(".element");
wm.set(element, "data");
let value = wm.get(elemet);
console.log(value); // data
element.parentNode.removeChild(element);
element = null;
除了Symbol
外,我们同样可以利用 WeakMap
实现对象的私有属性:
let privateData = new WeakMap();
class Person {
constructor(name) {
privateData.set(this, { name });
}
getName() {
return privateData.get(this).name;
}
}
let person = new Person('wozien');
console.log(person.getName()); // wozien