ES6学习-Set 集合与Map集合

Set集合是一种无重复元素大的列表,开发者一般不会逐一读取数组中的元素,也不太可能逐一访问Set集合的每一个元素,通常是检测所给元素是否在集合中存在。Map集合内含多组键值对,经常用于缓存频繁取用的数据。

ECMAScript5中的Set和Map集合

在ES5中,常用对象属性来模拟这两种集合。

var set=Object.create(null);
set.foo=true;
//检查属性是否存在
if(set.foo){
    //要执行的代码
}

这里的set是个原型为null的对象,不继承任何属性。在ES5中,开发者们用类似方法检查某个对象的属性值是否存在。
模拟Set和Map这两种集合对象的唯一区别是存储的值不同。

var map=Object.create(null);
map.foo="bar";
var value=map.foo;
console.log(value);//"bar"

这段代码将字符串"bar"存储在map.foo中。一般来说,Set集合常被用于检查对象中是否存在某个键名,而Map集合常被用来获取已存得信息。

该解决方案的一些问题

碰到对象属性的某些限制,上诉方法就会复杂。例如:所有对象的属性名必须是字符串类型,必须保证每个键名都是字符串类型且在对象中是唯一的。

var map=Object.create(null);
map[5]="foo";
console.log(map["5"]);//"foo"

例子中属性键名5被强制转换为了"5",如果想以数字为对象键名就会出问题了。

var map=Object.create(null),key1={},key2={};
map[key1]="foo";
console.log(map[key2]);//"foo"

由于例子中key1和key2将被转换为对象对应的字符串都是"[Object Object]",所以map[key2]和map[key1]其实引用同一个属性。这种错误很难被发现。
对于Map集合,如果它的属性值是假值,则要求使用布尔值的情况下会自动转换为false。强制转换在某些场景会出错.

var map=Object.create(null);
map.count=0;
if(map.count){
    //要执行的代码
}

在这里如果我们只是检查count值是否存在,则会出错,因为0虽然存在,但是会被转化为false,则导致出错。
在大型软件应用中,一旦发生这种问题将难以定位和调试.

ECMAScript6中的Set集合

ECMAScript中新增的Set类型是一种有序列表,其中含有一些相互独立的非重复值,通过Set集合可以快速访问里面的数据,更有效追踪各种离散值。

创建Set集合并添加元素

let set=new Set;//创建Set集合
set.add(5);//添加元素
set.add("5");//添加元素
console.log(set.size);//2,size属性可以返回当前的元素数量。

在Set集合中,不对所存值发生强制的类型转换。数字5和字符"5"是独立存在的(引擎调用Object.is()判断)。

let set=new Set(),key1={},key2={};
set.add(key1);
set.add(key2);
console.log(set.size);//2

多次调用add()方法来传入相同的值作为参数,后续的调用会被忽略。

let set=new Set;
set.add(5);
set.add("5");
set.add(5);
console.log(set.size);//2

Set函数可以用数组来初始化初始化,但是重读元素会被过滤掉,保证集合元素的唯一性。

let set=new Set([1,2,3,4,5,5,5,5,5]);
console.log(set.size);//5

实际上Set构造函数可以接受所有的迭代对象作为参数。
通过has()方法可以检测Set集合中是否含有某个值。

let set=new Set;
set.add(5);
set.add("5");
console.log(set.has(5));//true
console.log(set.has("5"));//true
console.log(set.has(6));//false,没有的元素会是false

移除元素

通过delete()可以移除Set集合中的某一元素,调用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.claer();
console.log(set.has("5"));//false
console.log(set.size);//0

Set集合中的forEach()方法

forEach()方法的回调函数接受三个参数:
·Set集合中下一次索引的位置
·与第一个参数一样的值
·被遍历的Set集合本身

let set=new Set([1,2]);
set.forEach(function(value,key,ownerSet) {
    console.log(key+" "+value);
    console.log(ownerSet===set);
});
//1 1
//true
//2 2
true

令人疑惑的是Set的这个方法中前两个参数都是一样的,其实这也可以解释得通,因为Set没有键名,但是设为2个参数,就和Map集合和数组的forEach方法区别太大了,所以统一为三个参数。

let set=new Set([1,2]);
let processor={
    output(value){
        console.log(value);
    },
    process(dataSet){
        dataSet.forEach(function(value){
            this.output(value);
        },this);
    }
};
processor.process(set);

在forEach方法中,第二个参数也与数组的一样,如果需要在回调中使用this调用,则可以将它作为第二个参数传入forEach()函数;

//箭头函数重写
let set=new Set([1,2]);
let processor={
    output(value){
        console.log(value);
    },
    process(dataSet){
        dataSet.forEach(value=>
            this.output(value));
    }
};
processor.process(set);

值得注意的是,尽管Set集合更适合用来追踪多个值,而且又可以通过forEach()方法操作每个参数,但是你不能像访问数组那样直接通过索引访问集合中的元素。如果有需要,可以转化为一个数组。

将Set集合转换为数组

数组转换为Set集合很简单,只要把数组传入Set构造函数就可以了,转换回去同样很简单,使用(...)运算符就可以了。

let set=new Set([1,2,3,3,3,4,5]),array=[...set];
console.log(array);//[1,2,3,4,5]

如果想要使数组变为无重复元素的数组,用这个方法就很简单了。

let test=[1,2,2,3,3,6];
(function eliminateDuplicates(items){
    return [...new Set(items)];
})(test)//[1,2,3,6]

Weak Set集合

将对象存储在Set的实例与存储在变量中完全一样,只要Set实例中的引用存在,垃圾回收机制就不能释放该对象的内存空间,于是之前提到的Set类型可以被看做是强引用的Set集合。

let set=new Set(),
    key={};
set.add(key);
console.log(set.size);//1
//移除原始引用
key=null;
console.log(set.size);//1
key=[...set][0];

Set集合会保留原始引用,这容易导致内存泄漏,ES6中引入了另外的一个类型:WeakSet集合。

创建Weak Set集合

//Weak Set集合支持三个方法:add()、has()、delete()
let set=new WeakSet(),key={};
//向集合set中添加对象
set.add(key);
console.log(set.has(key));//true
set.delete(key);
console.log(set.has(key));//false

两种类型的主要区别

let set=new WeakSet(),\
    key={};
set.add(key);
console.log(set.has(key));//true
key=null;//移除了

因为has()方法要传递强用,所以接下来无法验证了,但是JavaScript引擎会正确移除最后一个弱引用。
还有其它的差别:
1.在Weak Set的实例中,如果向add()、has()、delete()传入非对象参数会导致程序报错。
2.Weak Set集合不可迭代,不能用for-of循环。
3.Weak Set集合不暴露任何迭代器,所以无法通过程序自身来检测其中内容。
4.Weak Set集合不支持forEach()方法。
5.Weak Set集合不支持size属性。

ECMAScript中的Map集合

ECMAScript6中的Map类型是一种存储着许多键值对的有序列表。其中键名和对应的值都支持所有的数据类型。键名的等价性判断是通过调用Object.js()方法实现的。
如果需要添加新元素,可以使用set()方法,分别传入键名和对应值作为两个参数:如果要从集合中获取信息,并调用get()方法。

let map=new Map();
map.set("title","Understanding ECMAScript");
map.set("year",2016);
console.log(map.get("title"));//"Understanding ECMAScript"
console.log(map.get("year"));//2016
console.log(map.get("CCG"));//undefined

可以使用对象作为对象属性的键名。

let map=new Map(),
    key1={},
    key2={};
map.set(key1,5);
map.set(key2,42);
console.log(map.get(key1));//5
console.log(map.get(key2));//42

Map集合支持的方法

1.has(key)检测键名是否存在。
2.delete(key)移除键名和对应值。
3.clear()移除Map集合中所有的键值对。

let map=new Map();
map.set("name","Nicholas");
map.set("age",25);
console.log(map.size);//2
console.log(map.has("name"));//true
console.log(map.get("name"));//"Nicholas"
console.log(map.has("age"));//true
console.log(map.get("age"));//25
map.delete("name");
console.log(map.has("name"));//false
console.log(map.get("name"));//undefined
console.log(map.size);//1
map.clear();
console.log(map.has("name"));//false
console.log(map.get("name"));//undefined
console.log(map.has("age"));//false
console.log(map.get("age"));//undefined
console.log(map.size);//0

Map集合初始化方法

可以向Map构造函数传入数组来初始化一个Map集合,数组中的每个元素都是一个子数组,子数组包含一个键值对的键名与值两个元素。

let map=new Map([["name","Nicholas"],["age",25]]);
console.log(map.has("name"));//true
console.log(map.get("name"));//"Nicholas"
console.log(map.has("age"));//true
console.log(map.get("age"));//25
console.log(map.size);//2

Map集合的forEach()方法

1.Map集合的下一次索引的位置
2.值对应的键名
3.Map集合本身

let map=new Map([["name","Nicholas"],["age",25]]);
map.forEach(function (value,key,ownerMap) {
    console.log(key+" "+value);
    console.log(ownerMap===Map);
});
//name Nicholas
//true
//age 25
//true

Weak Map集合

Weak Map是弱引用的Map集合,也用于存储对象的弱引用。Weak Map集合中的键名必须是一个对象,如果使用非对象键名会报错;集合中保存的是这些对象的弱引用,如果在弱引用之外不存在其他强引用,就会被自动回收,同事也会移除Weak Map集合的键值对。但是只有集合中的键名遵从这个规则,键名对应的值如果是一个对象,则保存对象的强引用,不会触发垃圾收集。
Weak Map集合最大的用途就是保存Web页面的DOM元素。
使用这个这种方法的困难是:一旦清楚元素,如何通过库本身将对象清除。但是用Weak Map集合来跟踪DOM元素,这些库仍可以通过自定义对象整合,而且当DOM元素消失时,可以自动销毁相关对象。

使用Weak Map集合(这部分引用阮一峰老师的博客:http://es6.ruanyifeng.com/#docs/set-map#WeakMap

const wm = new WeakMap();
const element = document.getElementById('example');
wm.set(element, 'some information');
wm.get(element) // "some information"

上面代码中,先新建一个 Weakmap 实例。然后,将一个 DOM 节点作为键名存入该实例,并将一些附加信息作为键值,一起存放在 WeakMap 里面。这时,WeakMap 里面对element的引用就是弱引用,不会被计入垃圾回收机制。
也就是说,上面的 DOM 节点对象的引用计数是1,而不是2。这时,一旦消除对该节点的引用,它占用的内存就会被垃圾回收机制释放。Weakmap 保存的这个键值对,也会自动消失。
总之,WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏。
注意,WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。

const wm = new WeakMap();
let key = {};
let obj = {foo: 1};

wm.set(key, obj);
obj = null;
wm.get(key)
// Object {foo: 1}

上面代码中,键值obj是正常引用。所以,即使在 WeakMap 外部消除了obj的引用,WeakMap 内部的引用依然存在。

Weak Map集合支持的方法

1.has()检测给定的键值在集合中是否存在。
2.delete()移除制定的兼职对。
3.set()写入值。
4.get()得到值。

let map=new WeakMap(),
    element=document.querySelector(".element");
map.set(element,"Original");
console.log(map.has(element));//true
console.log(map.get(element));//"Original"
map.delete(element);
console.log(map.has(element));//false
console.log(map.get(element));//undefined

Weak Map集合中的使用方法和使用限制

1.如果只用对象作为对象的键名,Weak Map是最好的选择。
2.如果需要forEach()属性,size属性和clear()方法来管理集合中的元素,那么Map集合是一个更好的选择,只是要注意内存的使用情况。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,710评论 4 376
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,839评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,295评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,776评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,198评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,074评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,200评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,986评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,733评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,877评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,348评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,675评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,393评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,209评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,212评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,003评论 2 280

推荐阅读更多精彩内容