ES6新的数据结构 — Set
- Set类似于数组,但其成员是唯一的,没有重复值
<!-- 申明Set -->
var s = new Set();
var arr = [1,3,2,3,2,3,2,3,4,5,6,4,5];
arr.map(i => s.add(i)); // 调用s.add()方法给Set中添加值
// 或者在声明时,直接添加添加一个数组作为参数
var s1 = new Set(arr);
console.log(s); // Set(6) {1,3,2,4,5,6, size: 6, ...}
console.log([...s]); // [1,3,2,4,5,6]
- Set判断值是否相等使用精确相等运算符
var s2 = new Set([1,2,3,'2','a']);
console.log([...s2]); // [1,2,3,'2','a']
- Set的属性和方法:
<!-- 属性 -->
Set.prototype.constructor: 构造函数,默认是Set()
Set.size: 返回set实例的长度
<!-- 方法 -->
add(value): 添加值,返回Set结构本身
delete(value): 删除值,返回boolean
has(value): 判断值是否存在,返回boolean
clear(): 清空Set,无返回值
- Set的遍历操作
- Set.keys()键名遍历器
- Set.Values()键值遍历器,由于Set类似于数组,没有键名,所以键值与键名遍历器返回结果一致
- Set.entries()键值对遍历器
- forEach((x) => console.log(x))回调函数遍历
var s = new Set([1,2,3,'2','a']);
for(let item of s.key()) {
console.log(item); // 1 , 2 , 3 , '2' , 'a'
}
- 由于扩展运算符…的内部实现也是for…of循环,所以…同样适用Set数据结构
有同学可能好奇WeakSet结构
- WeakSet结构与Set类似,都不能有相同的值
- 不同之处在于:
- WeakSet的成员只能为对象,而且不能是其他的任何类型
- WeakSet对对象的引用是弱引用,及某对象在其他地方已经不使用了(WeakSet中仍旧引用该对象),系统的垃圾回收机制不会考虑WeakSet,直接回收该对象
- 由于以上两个不同,WeakSet是不可遍历的,也没有size属性
- 使用WeakSet的好处是,可以防止内存泄漏
ES6新的数据结构 — Map
- 玩过c/c++或java等语言的童鞋都应该知道map数据结构,存取数据都很方便。ES6以前,js要从一堆键值对中快速的取出某个键值对是要经过遍历或者转为对象的(取值也是通过遍历实现),并且对象的键一般只能使用字符串,在使用上有很大的限制
- Map提供的不再是Object中的键值对,而是值-值对的存取
var m = new Map();
<!-- 用字符串做键 -->
m.set(p, 'cont');
m.get(p); // cont
m.has(p); // true
m.delete(p); // true
m.has(p); // false
<!-- 用对象 -->
var obj = {p: 'cont'};
obj.p; // cont
m.set(obj, 'hello world!');
m.get(obj); // hello world!
- Map 也接受一个保存 值-值 对的数组作为其参数
var m = new Map([[a,'aaa'], [b, 'bbb']]);
m.get(a); // aaa
m.get(b); // bbb
- 如果对同一个键赋值多次,后面的值覆盖前面的值
var m = new Map([[a,'aaa'], [b, 'bbb']]);
m.set(a,'ccc');
m.get(a); // ccc
- Map的键引用的是一个地址,而不是像对象那样,引用的一个字符串。
<!-- 这里先废话几句 -->
var quote1 = ['a'];
var quote2 = {v: 'a'};
typeof quote1; // "object"
typeof quote2; // "object"
/**
* js中的"object"类型数据是引用类型数据,一个object就会占用一块内存,即使相同的值也是占用不同的内存
* 而其他的基础数据类型相同的值共用同一块内存
*/
<!-- 进入正题 -->
var m = new Map();
// 键名为基本数据类型
m.set(1, 'aaa');
m.set(1, 'bbb');
m.get(1); // bbb
// 键名为引用数据类型
m.set(['a'], 'aaa');
m.get(['a']); // undefined, get的键 ['a'], 和 set的键 ['a'],指的不是同一块内存,因此拿不到前面的 'aaa'
var k1 = ['a'];
var k2 = ['a'];
m.set(k1, 'aaa');
m.set(k2, 'bbb');
m.get(k1); // aaa
m.get(k2); // bbb, k1和k2指向的地址不用,存储的值也就不同
Map的键为基础数据类型时,只要值相等,Map就会认为是同一个键;Map的键为引用数据类型时,就算长得一模一样,但因为引用的内存地址不一样,还是会被当做两个键
+ Map实例的属性和方法
1. size属性,返回map结构的成员总数
2. set(key,value)方法,设置Map的键值,返回整个Map结构
3. get(key)方法,返回key对应的键值
4. has(key)方法,返回一个布尔值,表示该键值是否存在
5. delete(key)方法,删除key对应的键值,返回一个布尔值
6. 遍历方法:keys()、values()、entries()、forEach()遍历器
var m = new Map();
m.set(1,'aaa').set(3,'ads').set('aaa',231)
console.log(m.size); // 3
m.get(1); // aaa
console.log(m.has(3)); // true
for(let key of m.keys()) {
console.log(key); // 1 , 3 , 'aaa'
}
for(let en of m.entries()) {
console.log(en[0] + ': ' + en[1]); // 1: 'aaa' , 3: 'ads' , 'aaa':231
}
- Map转数组,使用object的 … 扩展运算符
let mapToArr = [...m];
console.log(mapToArr); // [[1, "aaa"],[3, "ads"],["aaa", 231]]
- 数组转Map:将数组传入Map的构造函数即可
let m1 = new Map(mapToArr);
console.log(m1); // Map(3) {1 => "aaa", 3 => "ads", "aaa" => 231}
- Map转对象(前提是Map的所有键都是字符串)采用循环遍历Map的键值存储到对象中,对象转Map同理
WeakMap
- WeakMap与Map的结构和用法基本类似,唯一的区别在于它只接受对象作为键名(null除外)
- WeakMap的键名是对象的弱引用,垃圾回收机制不将该引用考虑在内,当系统把对应的键值回收后,WeakMap自动删除该键值
var vm = new WeakMap();
var element = document.querySelector('.element');
vm.set(element, "Original");
vm.get(element); // Original
element.parentNode.removeChild(element); // 删除节点,系统会回收element这个Dom节点对象
element = null;
vm.get(element); // undefined, WeakMap自动删除了element这个成员
- WeakMap典型应用就是用来记录Dom节点的状态,当DOM节点被删除回收时,WeakMap会自动删除该DOM节点对应的成员,释放内存
- WeakMap的另一个应用就是部署内部私有属性,即键值引用另一个对象内的每个键或值或键值对,当该对象被删除,这个引用会被自动释放