其实数组也是集合, 只不过数组的索引是数值类型.当想用非数值类型做为索引时, 数组就没法知足须要了.数组
而 Map 集合能够保存多个键-值对(key-value), Set 集合能够保存多个元素.函数
对Map 和 Set 通常不会逐一遍历其中的元素. Map 通常用来存储须要频繁取用的数据, Set 通常用来判断某个值是否存在其中.post
在ES 5 中,没有 Set和Map集合, 通常使用对象来模拟这两种集合, 对象的属性做为键(key), 以属性值做为值(value), 即以 property: property-value
来模拟 key-value
的形式. 具体实现以下:ui
模拟 Map 的键值对集合:spa
// 建立一个 Map 对象
var map = Object.create(null);
// 添加属性和属性值, 即 添加 key 和 value
map.key1 = 'value 1';
map.key2 = {};
// 取得 key 对应的 value
console.log(map.key1); // "value 1"
console.log(map.key2); // "Object {}"
复制代码
模拟 Set :3d
// 建立一个 Set 对象
var set = Object.create(null);
// 添加属性和属性值, 即 添加 key 并令其值为 true, 即表示这个key存在于集合中
set.key = true;
// 判断 key 是否存在, 而后进行下一步的操做
if(set.key) { ... }
复制代码
下面正式来讨论这两种集合的特色code
Map 中存储的是 key-value 形式的键值对, 其中的 key 和 value 能够是任何类型的, 即对象也能够做为 key . 这比用对象来模拟的方式就灵活了不少对象
// 建立一个空的 Map
let map = new Map();
复制代码
// 用数组来建立一个 非空的 Map
let array = [ // 定义一个二维数组, 数组中的每子数组都有两个元素
['key1' , 'value 1'], // key 是 字符串 "key1", value 是字符串 "value 1"
[{} , 10086] , // key 是个对象, value 是数值 10086
[ 5, {} ] // key 是个数值类型, value 是对象
];
let map = new Map(array); // 将数组传入 Map 构造函数中
复制代码
set(key, value)
: 向其中加入一个键值对get(key)
: 若不存在 key 则返回 undefined
has(key)
:返回布尔值delete(key)
: 删除成功则返回 true, 若key不存在或者删除失败会返回 falseclear()
: 将所有元素清除和数组的 forEach 方法相似, 回调函数中都包含3个参数 值, 键, 和 调用这个方法的 Map 集合自己索引
map.forEach(function(value, key, ownerMap){
console.log(key, value); // 每对键和值
console.log(ownerMap === map); // true
});
复制代码
Set 和 Map 最大的区别是只有键 key 而没有 value, 因此通常用来判断某个元素(key)是否存在于其中.生命周期
既能够建立一个空 set 也能够用数组来初始化一个非空的set. 和 Map 不一样的是, 数组是一维数组, 每一个元素都会成为 set 的键.例如:
// 建立一个数组
let array = [1, 'str']; // 一维数组
// 用数组来初始化 set
let set = new Set(array);
复制代码
add(key)
: 往set添加一个元素, 若是传入多个参数, 则只会把第一个加入进去let set = new Set();
set.add(1, 2, 3);
console.log(set.has(1), set.has(2), set.has(3)); // true false false 能够看到只有第一个参数被加入进了 set
复制代码
has(key)
delete(key)
clear()
和 Map 的 forEach 方法类似, 回调函数的参数也是3个 (value, key, ownerSet). 按道理来讲由于 set 中只有 key 没有 value, 那么回调函数中不该该存在 value 这个参数呀, 那为何还会有 value 这个参数呢?多是由于 另外两种集合 ( 数组和 Map ) 的 forEach 方法的回调函数的参数都是这三个, 若是对于 Set 不是这样, 那么这三种集合的 forEach 函数就会丢失了一致性. 这个理由......
那么既然没有 value , 那么这个value的值是什么呢?答案是: value的值 === key的值 ,也就是说 在这里 value 就是 key, 只不过在字面上写成了 value 而已. 为了保持一致性嘛
下面这段代码能够验证这个说法.
set.forEach(function(value, key, ownerSet){
console.log(value === key, set === ownerSet); // true true
});
复制代码
这两个集合比以前的两个集合在名字以前都加上了 Weak
, 这个 Weak
能够直译成弱
, 这个弱指的是弱引用, 那么前面不带Weak的 Set 和 Map 就是不弱, 也就是强了. 这个强指的是强引用, 再具体一点就是对于 key 位置的对象的强引用. 下面会详细讨论.
add, has, delete
方法可用; WeakMap 只有 set, has, get, delete
方法可用.强弱版本对于 key 是对象时的引用机制以下:
将对象设置为 key 时, 就在集合中保存了这个对象的引用. 当这个对象没有其余引用了的时候, ( 即只有集合还引用着这个对象的时候 ), 弱类型的集合会放弃对这个对象的引用, 把这个对象从集合里移除, 不让它继续存在于集合中了, 这样一来, 这个对象就会被垃圾回收了, 很有些“赶尽杀绝”的意思; 可是强类型的集合还会一直保存着对这个对象的引用, 就把它一直放在集合里.这就是 [WeakSet 和 WeakMap] 与 [Set 和 Map] 的根本区别.
要注意的是这个机制只做用于 key , 而 value 位置绑定的对象不管是否还存在别的引用, WeakMap 都不会放弃这个对象. 只有这个位置的 key 绑定的对象没有其余引用时, 才会把 key 和 value 都放弃. 是否放弃的决定权在于 key 位置.
若版本集合能够用在须要生命周期管理的地方,例如保存对一个 DOM 对象的引用, 若是一个 DOM 对象使用完毕, 没有其余的引用了, 那么它应该被 垃圾回收,以避免产生内存泄漏,那么弱版本的集合就最适合用来保存这样的对象了。