Javascript除了提供对象和数组来作为集合存储数据,还提供了Map、Set这类数据结构来作为集合存放数据

Map

基本方法


Map是一个集合对象,下面是常用的方法:


(1)new Map():创建一个map对象;


(2)map.set(key,value):存储一个键值对;


(3)map.get(key):返回键所对应的值,如果不存在则返回undefined;


(4)map.has(key):若存在键值对返回true,否则返回false;


(5)map.delete(key):移除键值对;

(6)map.clear():清除map的所有数据;


(7)map.size:返回map的元素数量;



例子:

let map = new Map();

map.set('1', 'str1');   // a string key
map.set(1, 'num1');     // a numeric key
map.set(true, 'bool1'); // a boolean key

// remember the regular Object? it would convert keys to string
// Map keeps the type, so these two are different:
alert( map.get(1)   ); // 'num1'
alert( map.get('1') ); // 'str1'

alert( map.size ); // 3



Map也可以使用对象作为键,例如:


let john = { name: "John" };

// for every user, let's store his visits count
let visitsCountMap = new Map();

// john is the key for the map
visitsCountMap.set(john, 123);

alert( visitsCountMap.get(john) ); // 123



ps:Map是如何比较键的值呢?

Map内使用SameValueZero算法来进行key值的比较,大致上于===类似,不过有一点不同的是,Map可以比较NaN,而===不行,这也是为什么Map可以使用NaN作为键名


Map内部方法使用链式结构,例如:

map.set('1', 'str1')
  .set(1, 'num1')
  .set(true, 'bool1');



对象映射


在创建Map对象时,我们可以传入键值对格式的数组对象进行初始化,例如:


// array of [key, value] pairs
let map = new Map([
  ['1',  'str1'],
  [1,    'num1'],
  [true, 'bool1']
]);

但是还有一种方法就是使用Obejct.entries(),它会将对象映射为键值对格式的数组对象,例如:


let map = new Map(Object.entries({
  name: "John",
  age: 30
}));



Map迭代



迭代Map提供了三种方法来迭代map对象,如下:



(1)map.keys():返回键的迭代器;



(2)map.values():返回值的迭代器;



(3)map.entries():返回键值对的迭代器,类似于for...of;




例子:


let recipeMap = new Map([
  ['cucumber', 500],
  ['tomatoes', 350],
  ['onion',    50]
]);

// iterate over keys (vegetables)
for (let vegetable of recipeMap.keys()) {
  alert(vegetable); // cucumber, tomateos, onion
}

// iterate over values (amounts)
for (let amount of recipeMap.values()) {
  alert(amount); // 500, 350, 50
}

// iterate over [key, value] entries
for (let entry of recipeMap) { // the same as of recipeMap.entries()
  alert(entry); // cucumber,500 (and so on)
}



Map也提供forEach()方法来遍历,例如:


recipeMap.forEach( (value, key, map) => {
  alert(`${key}: ${value}`); // cucumber: 500 etc
});



Set



Set集合类似于数组,不过Set规定存储的元素不能重复,如果出现重复,则后加入的元素将覆盖前一个元素,下面是常用的方法:



(1)new Set(iterable):创建一个set对象,可选地传入可迭代的对象,例如数组;



(2)set.add(value):添加元素到set中;



(3)set.delete(value):移除set中的元素,如果元素存在返回true,不存在返回false;



(4)set.has(value):存在元素返回true,否则返回false;



(5)set.clear():清除所有元素;



(6)set.size:返回set元素个数;





例子:



let set = new Set();

let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };

// visits, some users come multiple times
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);

// set keeps only unique values
alert( set.size ); // 3

for (let user of set) {
  alert(user.name); // John (then Pete and Mary)
}



Set迭代



例子:



let set = new Set(["oranges", "apples", "bananas"]);

for (let value of set) alert(value);

// the same with forEach:
set.forEach((value, valueAgain, set) => {
  alert(value);
});





WeakMap 和 WeakSet


JavaScript垃圾回收是一种内存管理技术。在这种技术中,不再被引用的对象会被自动删除,而与其相关的资源也会被一同回收。Map和Set中对象的引用都是强类型化的,并不会允许垃圾回收。这样一来,如果Map和Set中引用了不再需要的大型对象,如已经从DOM树中删除的DOM元素,那么其回收代价是昂贵的。例如:


let john = { name: "John" };

let map = new Map();
map.set(john, "...");

john = null; // overwrite the reference

// john is stored inside the map
// we can get it by using map.keys()





为了解决这个问题,ES6还引入了另外两种新的数据结构,即称为WeakMap和WeakSet的弱集合。这些集合之所以是“弱的”,是因为它们允许从内存中清除不再需要的被这些集合所引用的对象,WeakMap 最大的好处是可以避免内存泄漏。一个仅被 WeakMap 作为 key 而引用的对象,会被垃圾回收器回收掉。。WeakMap于Map不同的是,WeakMap要求key值必须为对象,不能是原型,例如:



let weakMap = new WeakMap();

let obj = {};

weakMap.set(obj, "ok"); // works fine (object key)

weakMap.set("test", "Whoops"); // Error, because "test" is a primitive



let john = { name: "John" };

let visitsCountMap = new WeakMap();

visitsCountMap.set(john, 123);

// now john leaves us, we don't need him anymore
john = null;

// there are no references except WeakMap,
// so the object is removed both from the memory and from visitsCountMap automatically



当john=null时,john所指的对象将被回收,同时WeakMap对象里的john也被移除。


weakMap.get(key)weakMap.set(key, value)weakMap.delete(key, value)weakMap.has(key)