新的数据结构
目录:
- 新的数据结构
- 无序集合 Set
- 数组与集合的区别
- 增减元素
- 检查元素
- 遍历元素
- forEach
- for-of
- WeakSet
- Map映射类型
- WeakMap
无序集合 Set
数组与集合的区别
数组 | 集合 | |
元素序列 | 有序 | 无序 |
元素可重复性 | 元素可重复 | 元素不可重复 |
用法:new Set([iterable]) : Set
const set = new Set();
const set = new Set([1, 2, 3]);
增减元素
通过 add、delete 和 clear 方法来添加、删除和清空集合内的元素。
const set = new Set();
// 添加元素
set
.add(1)
.add(2)
.add(3)
.add(3); // 这一句并不会起到任何作用,因为元素3已存在
console.log(set); // Set(3) {1, 2, 3}
// 删除元素
set.delete(2);
console.log(set); // Set(2) {1, 3}
// 清空集合
set.clear();
console.log(set); // Set(0) {}
检查元素
因为集合并没有排序的概念,所以集合对象并没有像数组对象那样有 indexOf 方法,也就意味着不能通过 set.indexOf(value) >= 0 的形式来检查元素是否存在于集合中。但集合对象提供了 has 方法用于检查某集合中是否包含某一个元素。
const set = new Set([1, 2, 3, 4]);
//检查元素
set.has(2); // true
set.has(5); // false
遍历元素
forEach
集合对象的 forEach 方法和数组类型的一样,传入一个回调函数以接受集合内的元素,并且可以为这个回调函数指定一个上下文,同样的,集合对象的 forEach 方法也是无法被中断的。
setObj.forEach(callbackfn[, thisArg])
- setObj: 必需。Set 对象。
- callbackfn: 必需。callbackfn 最多接受三个参数。对于集合中的每个元素,forEach 都会调用函数一次。
- thisArg:可选。可在 callbackfn 函数中为其引用 this 关键字的对象。如果省略 thisArg,则 undefined 将用作 this 值。
注意无法为箭头函数指定一个上下文。
function callbackfn(value, key, setObj)
回调参数 | 定义 |
Value | 集中包含的值 |
key | 集中包含的值。一组没有键,因此此值与 value 相同 |
setObj | 要遍历的 Set 对象 |
const set = new Set([1, 2, 3, 4]);
set.forEach(item => {
console.log(item);
});
// 1
// 2
// 3
// 4
set.forEach(function(item){
console.log(item * this.foo);
}, {foo: 2});
// 2
// 4
// 6
// 8
for-of
for-of 循环语句可以迭代可迭代对象(Iterable Object),并可以配合 const 和 let 使用,从而解决 forEach 方法不可中断的问题。
const set = new Set([1, 2, 3, 4]);
for(const val of set){
console.log(val);
}
// 1 2 3
WeakSet
const weakset = new WeakSet();
weakset.add(1); // Uncaught TypeError: Invalid value used in weak set
weakset.add({foo: 1});
console.log(weakset); // WeakSet {}
console.log(weakset.size); // undefined
WeakSet 与 Set 不同的地方:
- WeakSet 不能包含值类型元素,否则会抛出一个 TypeError。
- WeakSet 不能包含无引用的对象,否则会自动清除出集合。
- WeakSet 无法被探知其大小,也无法被探知其中所包含的元素。
const weakset = new WeakSet();
let foo = {bar: 1};
weakset.add(foo);
console.log(weakset.has(foo)); // true
foo = null;
console.log(weakset.has(foo)); // false
实现将字符串等值类型加入到 WeakSet 数据结构中:
var ws = new WeakSet();
var str = new String("Hello");
var num = new Number(1);
ws.add(str);
ws.add(num);
console.log(ws.has(str)); // true
console.log(ws.has(num)); // true
这种做法的弊端在于,被加入到 WeakSet 中的字符串和数字等不能被修改,因为一旦进行修改其引用便会丢失,甚至导致被移除出集合。
str += "hehe";
console.log(ws.has(str)); // false
WeakSet 最大的实用意义在于,可以让我们直接对引擎中垃圾收集器的运行情况有程序化的探知方式,开发者可以利用 WeakSet 的特性以更高的定制化方案来优化程序的内存使用方案。
Map映射类型
用法:new Map([iterable]) : Map
const map = new Map();
在创建映射对象时,可以将一个以二元数组(键值对)作为元素的数组传入到构建函数中,其中每一个键值对都会加入到该映射对象中。该数组内的元素会以数组顺序进行处理,如果存在相同的键,则会按照先进先出(FIFO)原则,以该键最后一个处理的对应值为最终值。
const map = new Map([['foo', 1], ['foo', 2]]);
console.log(map.get('foo')); // 2
映射对象操作方法
操作方法 | 方法内容 |
set(key, value) | 添加键值对到映射中 |
get(key) | 获取映射中某一个键的对应值 |
delete(key) | 将某一键值对移除出映射中 |
clear() | 清空映射中所有的键值对 |
entries() | 返回一个以二元数组(键值对)作为元素的数组 |
has(key) | 检查映射中是否包含某一键值对 |
keys() | 返回一个一当前映射中所有键作为元素的可迭代对象 |
values() | 返回一个一当前映射中所有值作为元素的可迭代对象 |
map.size | 映射中键值对的数量 |
映射对象和 Object 的区别
映射对象 Map | 普通对象 Object | |
存储键值对 | √ | √ |
对遍历所有键值 | √ | √ |
检查是否包含指定键值对 | √ | √ |
使用字符串(String)作为键 | √ | √ |
使用 Symbol 作为键 | √ | √ |
使用任意对象作为键 | √ | |
可以方便地得知键值对的数量 | √ |
除了语法和实现层面上的区别之外,映射对象和普通对象的区别还体现在 JSON 的序列化结果中。Object 的 JSON 序列化结果是标准的对象字面量形式,而 Map 的 JSON 序列化结果是以关联数组的形式表达。
const map = new Map();
map.set('foo', 1);
map.set('bar', 2);
const str = JSON.stringify(map);
console.log(str); // [["foo",1],["bar",2]]
// ...data transport
const otherMap = new Map(JSON.parse(str));
console.log(otherMap.get('bar')); // 2
WeakMap
和 WeakSet 类似,只不过 WeakMap 的键会检查变量引用,只要其中一个引用全被解除,该键值对就会被删除。
const weakmap = new WeakMap();
let keyObject = {id: 1};
const valObject = {score: 100};
weakmap.set(keyObject, valObject);
weakmap.get(keyObject); // {score: 100}
keyObject = null;
console.log(weakmap.has(keyObject)); // false