4-3集合
集合(set)是在ES6中引入的一种数据结构,用于表示唯一值的集合,所以它不能包含重复值。接 下来这一小节,就让我们具体来看一下这种新的数据结构。
4-3-1什么是集合
在ES6标准制定以前,可选的数据结构类型有限,可以说只有数组这种数据结构。而数组使用的 又是数值型索引,因而经常被用于来模拟队列和栈的行为。但是如果需要使用非数值型索引,就 会用非数组对象创建所需的数据结构,而这就是Set集合与后面一节要介绍的Map映射的早期实 现。
Set集合是一种无重复元素的列表,这是这种数据结构的最大的一个特点。
4-3-2创建集合
要创建一个集合,方法很简单,直接使用new就可以创建一个Set对象。如果想要集合在创建时 就包含初始值,那么我们可以传入一个数组进去。
let s1 = new Set();
let s2 = new Set([1,2,3]);
console.log(s1);//Set {}
console.log(s2);//Set { 1, 2, 3 }
4-3-3给集合添加值
使用add()方法可以给一个集合添加值,由于调用add()方法以后返回的又是一个Set对象,所 以我们能连续调用add()方法进行值的添加,这种像链条一样的方法调用方式被称为链式调 用。
let s1 = new Set();
s1.add(1);
console.log(s1);//Set { 1 }
s1.add(2).add(3).add(4);
console.log(s1);
//Set { 1, 2, 3, 4 } 我们还可以直接将一个数组传入add()方法里面
let s1 = new Set();
s1.add([1,2,3]);
console.log(s1);
//Set { [ 1, 2, 3 ] }
但是需要注意的是建立Set对象时传入数组与调用add()方法时传入数组是效果是不一样,区别 如下:
建立Set对象时传入数组,数组每一项成为Set对象的一个元素
let s1 = new Set([1,2,3]);
console.log(s1);//Set { 1, 2, 3 } console.log(s1.size);//3
调用add()方法时传入数组,就是作为Set对象的一个元素
let s1 = new Set();
s1.add([1,2,3]);
console.log(s1);//Set { [ 1, 2, 3 ] } console.log(s1.size);//1
在Set对象中,不能够添加相同的元素,这是很重要的一个特性
let s1 = new Set();
s1.add(1).add(2).add(2).add(3);
console.log(s1);
//Set { 1, 2, 3 }
4-3-4集合相关属性和方法
- 用size属性获取元素个数
let s1 = new Set([1,2,3]);
console.log(s1.size);//3
- 使用hasO方法来查看一个集合中是否包含某一个值
let s1 = new Set([1,2,3]);
console.log(s1.has(1));//true
- 删除集合值
使用delete删除Set对象里面的某一个元素
let s1 = new Set([1,2,3]);
s1.delete(2);
console.log(s1);//Set { 1, 3 }
//没有的元素也不会报错
s1.delete("2");
console.log(s1);//Set { 1, 3 }
如果要一次性删除所有的元素,可以使用clear方法
let s1 = new Set([1,2,3]);
s1.clear()
console.log(s1);//Set {}
4-3-5遍历集合
集合也是可以枚举的,我们同样可以使用for-o f来对集合进行遍历,如下:
let s = new Set([1,2,3,4,5]);
for(let i of s){ console.log(i);
}
// 1
// 2
// 3
// 4
// 5
或者通过for Each来进行遍历,示例如下:
//使用forEach进行遍历
let s = new Set([1,2,3,4,5]); s.forEach(ele => console.log(ele));
// 1
// 2
// 4
// 5 除此之外,我们也可以使用集合里面自带的keys() , values()以及ent ries()方法来对集合 进行遍历。顺便要说一下的是,在集合里面键和值是相同的。
keys()方法遍历集合的键
let s = new Set(["Bill","Lucy","David"]); for(let i of s.keys()){
console.log(i);
}
// Bill
// Lucy
// David
values()方法遍历集合的值
let s = new Set(["Bill","Lucy","David"]); for(let i of s.values()){
console.log(i);
}
// Bill
// Lucy
// David
ent ries()方法同时遍历集合的键与值
let s = new Set(["Bill","Lucy","David"]); for(let i of s.entries()){
console.log(i);
}
// [ 'Bill', 'Bill' ]
// [ 'Lucy', 'Lucy' ]
// [ 'David', 'David' ]
4-3-6集合转数组
将集合转为数组,最快的方法就是使用前面所讲过的扩展运算符,如下:
let s1 = new Set([1,2,3]);
console.log(s1);//Set { 1, 2, 3 }
let arr = [...s1]; console.log(arr);//[ 1, 2, 3 ]
除此之外,我们还可以使用Arra y对象所提供的from。方法来进行转换
let s1 = new Set([1,2,3]); console.log(s1);//Set { 1, 2, 3 }
let arr = Array.from(s1); console.log(arr);//[ 1, 2, 3 ]
前面我们有提到过,Set对象里面是不能够存放相同的元素的,利用这个特性,我们可以快速的 为数组去重,如下:
let arr = [1,2,2,3,4,3,1,6,7,3,5,7];
let s1 = new Set(arr);
let arr2 = [...s1]; console.log(arr2);//[ 1, 2, 3, 4, 6, 7, 5 ]
4-3-7弱集合(扩展)
当对象添加到集合中时,只要集合存在,它们就一直存储在集合。即使对象的引用被删除了也依 然如此,我们来看下面的这个例子:
let arr = [1,2,3];
let s = new Set(arr);
arr = null;//删除arr数组的指向
console.log(s);//Set { 1, 2, 3 }数组依然存在于集合中 console.log(arr);//null
可以看到,这里我们删除了对数组的引用,但是该数组依然存在,只不过里面的值为n ull,这样 的话垃圾回收就不会不会回收这个数组,从而可能会引起内存泄漏
什么是内存泄漏?
—个程序里面保留着已经不能在内存中访问的值时,就会发生内存泄露,也就是说占着空间却没 用,造成内存的浪费。
例如:
let arr = [1,2,3]; arr = null;
断开了arr对1, 2, 3的引用,现在1, 2, 3在内存里面已经是垃圾了。内存泄露会逐渐减少全部 可用内存,导致程序和系统的速度变慢甚至崩溃
那么怎样才能清空这些没用的数据呢?例如上例中的1, 2, 3。事实上在JavaScript中采用的是 动态内存管理技术,比如垃圾回收机制,会自动从内存中删除不再被程序需要的东西。而有些编 程语言,例如C++,则是需要程序员手动的管理内存,在某些东西完成任务之后,将其从内存中 删除。
那么,集合的问题就在于即使失去了引用,也不会被垃圾回收,这个时候我们可以使用弱集合来 避免这种状况。创建弱集合使用new运算符和WeakSet()
let weak = new WeakSet();
弱集合无法添加基本数据类型,也就是说无法像集合那样添加简单值进去。
let weak = new WeakSet();
weak.add(1);
//TypeError: Invalid value used in weak set
除了这个限制以外,弱集合和普通集合还有一些细微的区别,例如无法在创建弱集合时传入一个 数组进行初始化。不过弱集合也拥有has() , add() , delete()等方法。
let arr = [1,2,3,4,5];
let weak = new WeakSet(arr);
//TypeError: Invalid value used in weak set
//无法在创建弱集合时传入一个数组进行初始化
还需要注意一点的是,弱集合是对对象的弱引用,所以不能访问对象里面的值列表。这使得弱集 合看上去像是空的,但是并不是空的,证明如下:
let weak = new WeakSet();
let arr = [1,2,3];
weak.add(arr);
console.log(weak);//WeakSet {}
console.log(weak.has(arr));//true
4-4映射
映射(Map)也是ES6规范中引入的一种数据结构。这是一种存储键-值对列表很方便的方法,类似 于其他编程语言中的词典或者哈希表。这一小节,让我们一起来看一下映射这种数据结构。
4-4-1什么是映射
JavaScript的对象(Object),本质上是键值对的集合(Hash结构),但是传统上只能用字符串当作 键。这给它的使用带来了很大的限制。俄们会在第6章学习JavaScript里面的对象)
为了解决这个问题,ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是"键"的 范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构(对象结构)提 供了 "字符串一值啲对应,而Map结构提供了"值一值啲对应,是一种更完善的H ash结构的实 现。
4-4-2创建映射
使用new关键字与Map()构造函数,就可以创建一个空的m ap对象。如果要向Map映射中添加 新的元素,可以调用set()方法并分别传入键名和对应值作为两个参数。如果要从集合中获取 信息,可以调用get()方法。
let m = new Map();
m.set("name","xiejie");
m.set("age",18);
console.log(m);
//Map { 'name' => 'xiejie', 'age' => 18 }
console.log(m.get("name"));
//xiejie
在对象中,无法用对象作为对象属性的键名。但是在Map映射中,却可以这样做,可以这么说, 在Map映射里面可以使用任意数据类型来作为键。
let m = new Map();
m.set({},"xiejie"); m.set([1,2,3],18);
m.set(3581,18);
console.log(m);
//Map { {} => 'xiejie', [ 1, 2, 3 ] => 18, 3581 => 18 }
传入数组来初始化Map映射 可以向Map构造函数传入一个数组来初始化Map映射,这一点同样与Set集合相似。数组中的每 个元素都是一个子数组,子数组中包含一个键值对的键名与值两个元素。因此,整个Map映射中 包含的全是这样的两个元素的二维数组
let arr = [["name","xiejie"],["age",18]];
let m = new Map(arr);
console.log(m);
//Map { 'name' => 'xiejie', 'age' => 18 }
4-4-3映射相关属性和方法
在设计语言新标准时,委员会为Map映射与Set集合设计了如下3个通用的方法 has(key):检测指定的键名在Map映射中是否已经存在 delete(key):从Map映射中移除指定键名及其对应的值
clear。:移除M ap映射中的所有键值对
Map映射同样支持size属性,其代表当前集合中包含的键值对数量
let arr = [["name","xiejie"],["age",18]]; let m = new Map(arr);
console.log(m);//Map { 'name' => 'xiejie', 'age' => 18 } console.log(m.size);//2 console.log(m.has("name"));//true console.log(m.get("name"));//xiejie m.delete("name");
console.log(m);//Map { 'age' => 18 } m.clear();
console.log(m);//Map {}
4-4-4映射的遍历
与集合一样,映射也是可以枚举的,所以可以用与集合类似的方式进行遍历。 使用for-of来遍历映射
let m = new Map([["name","xiejie"],["age",18]]); for(let i of m){
console.log(i);
}
// [ 'name', 'xiejie' ]
// [ 'age', 18 ]
keys()方法遍历映射的键
let m = new Map([["name","xiejie"],["age",18]]); for(let i of m.keys()){
console.log(i);
}
// name
// age
values()方法遍历映射的值
let m = new Map([["name","xiejie"],["age",18]]); for(let i of m.values()){
console.log(i);
}
// xiejie
// 18
ent ries()方法同时遍历映射的键与值
let m = new Map([["name","xiejie"],["age",18]]); for(let i of m.entries()){
console.log(i);
}
// [ 'name', 'xiejie' ]
// [ 'age', 18 ]
4-4-5映射转为数组
Map结构转为数组结构,比较快速的方法还是使用前面介绍过的扩展运算符...。
let arr = [["name","xiejie"],["age",18]];
let m = new Map(arr);
console.log([...m.keys()]);//[ 'name', 'age' ]
console.log([...m.values()]);//[ 'xiejie', 18 ]
console.log([...m.entries()]);//[ [ 'name', 'xiejie' ], [ 'age', 18 ] ]
console.log([...m]);//[ [ 'name', 'xiejie' ], [ 'age', 18 ] ] 或者使用Array对象的from。方法
let arr = [["name","xiejie"],["age",18]];
let m = new Map(arr);
console.log(Array.from(m));
//[ [ 'name', 'xiejie' ], [ 'age', 18 ] ]
4-4-6弱映射(扩展)
弱映射和弱集合很类似,主要是解决存在映射里面的垃圾数据问题,创建弱映射使用new运算符 以及WeakMap()构造器
let weakMap = new WeakMap();
弱映射和普通映射一样,同样也具有has() , get() , set() , delete()等方法。
- 所谓数据结构,就是计算机存储和组织数据的方式。主要就是指将数据以什么样的结构存储 到计算机里面。
- 数组是最常见的一种数据结构。在JavaScript里面创建数组可以通过字面量和构造函数两种 方式来进行创建。
- 创建数组以后我们就可以对该数组进行赋值,访问和删除等操作。
- 解构是指将一个复杂类型的数据分解为普通类型的数据,在JavaScript中可以对数组和对象 进行解构。
- JavaScript中并不支持多维数组,但是我们可以利用动态语言的特性模拟出多维数组。
- ES6中新添加的扩展运算符,可以快速用于取出可迭代对象的每一项。
- 数组也拥有许多的属性和方法,掌握好这些属性和方法,可以让我们编写程序时事半功倍。
- 集合(Set)是ES6中新引入的一种数据结构,它最大的特点就是不能包含重复值。
9•映射(Map)也是ES6中新引入的一种数据结构,它是一种键值对结构,其最大的特点在于键 可以是任意数据类型。