目录
Set
Set类似于数组,但是成员的值都是唯一的,没有重复的值
Set本身是一个构造函数,可以接受一个数组(或者具有iterable接口的其他数据结构)作为参数。
去除数组重复成员的方法
- 扩展运算符
[...new Set(array)]
- Array.from方法可以将Set结构转为数组
Array.from(new Set(array))
注意点
- 向Set加入值时,不会发生类型转换。在Set内部判断两个值是否不同,类似于精确相等运算符(=),区别是:NaN等于自身,而=认为NaN不等于自身。
let set = new Set();
set.add(NaN);
set.add(NaN);
console.log(set);//Set {NaN}
console.log(NaN === NaN); //false
- 注意:两个对象总是不相等============
//由于两个空对象不相等,所以它们被视为两个值
let set = new Set();
set.add({});
console.log(set.size);
set.add({});
console.log(set.size);
Set实例的属性和方法
属性
- Set.prototype.constructor:构造函数,默认就是Set函数
- Set.prototype.size:返回Set实例的成员总数
方法
操作方法
- add(value) 添加某个值,返回Set结构本身
s.add(1).add(2);
- delete(value) 删除某个值,返回布尔值,表示删除是否成功
- has(value) 判断该值是否为Set的成员,返回布尔值
- clear() 删除所有成员,没有返回值
遍历方法
Set的遍历顺序是插入顺序
- keys() 返回键名的遍历器
- values() 返回键值的遍历器
- entries() 返回键值对的遍历器
- forEach() 使用回调函数遍历每个成员
前三个方法返回的都是遍历器对象,Set结构的键值和键名是同一个值(keys和values方法的行为完全一致)(也可以理解为没有键名,只有键值)
Set结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法。因此,可以直接用for…of循环遍历Set。
Set.prototype[Symbol.iterator] === Set.prototype.values //true
forEach()用于对每个成员执行某种操作,没有返回值,表示绑定处理函数内部的this对象
mySet.forEach(callback[, thisArg])
扩展运算符内部使用for…of循环
数组的map和filter方法也可以用于Set
想在遍历操作中,同步改变原来的Set结构
let set = new Set([1, 2, 3]);
set = new Set([...set].map(val => val * 2));
set = new Set(Array.from(set, val => val > 2));
- Array.from() 方法对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
Array.from(arrayLike[, mapFn[, thisArg]])
arrayLike 想要转换成数组的伪数组对象或可迭代对象。
mapFn 可选如果指定了该参数,新数组中的每个元素会执行该回调函数。
WeakSet
WeakSet作为构造函数,可以接受一个数组或者类似数组的对象(具有Iterable接口的对象)作为参数。数组的成员会自动成为WeakSet实例对象的成员。(数组的成员只能是对象,因为WeakSet的成员只能是对象)
WeakSet结构与Set类似,但它与Set有两个区别
与Set的区别
- WeakSet的成员只能是对象,不能是其他类型的值。
- WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象是否还存在与WeakSet中。
注意点
- WeakSet的成员是不适合引用的,因为它随时会消失。
- WeakSet不可遍历,因为WeakSet内部有多少个成员,取决于垃圾回收处理机制有没有运行,而且垃圾回收处理机制何时运行是不可预测的。因为成员都是弱引用,随时可能消失。
适用场景
适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在WeakSet里面的引用就会自动消失。
WeakSet的用处,存储DOM节点,不用担心节点从文档移除时,会发生内存泄漏。
WeakSet结构的方法
(没有size属性,没有办法遍历它的成员,无法清空)
- add(value) 添加新成员
- delete(value) 清除某个指定成员
- has(value) 判断某个值是否在WeakSet实例中
Map
js的对象本质上是键值对的集合,只能用字符串当做键。Map类似于对象,也是键值对的集合。但“键”的范围不限于字符串,各种类型的值都可以当做键。
Map也可以接受一个数组作为参数,该数组的成员是一个个表示键值对的数组。任何具有Iterator接口、且每个成员都是一个双元素的数组的数据结构都可以作为Map构造函数的参数。Set和Map都可以用来生成新的Map。
const map = new Map([
['name', '张三'],
['age', '18'],
]);
map.size //2
map.has('name') //true
map.get('name') //'张三'
注意点
只有对同一个对象的引用,Map结构才将其视为同一个键。Map的键实际上是跟内存地址绑定的,只要内存地址不一样就视为两个键。这就解决了同名属性碰撞的问题。
// 实际上是两个键
let map = new Map();
map.set(['a'], 555);
console.log(map.get(['a']));// undefined
let map1 = new Map();
let array = ['a'];
map1.set(array, 555);
console.log(map1.get(array));// 555
let map2 = new Map();
let k1 = ['a'];
let k2 = ['a'];
map2.set(k1, 111).set(k2, 222);
console.log(map2.get(k1));// 111
console.log(map2.get(k2));// 222
Map实例的属性和方法
属性
size属性,返回Map结构的成员总数。
方法
操作方法
- set(key, value) 设置键名key对应的键值为value,返回整个Map结构。
let map = new Map(); map.set('foo',true).set('bar',false);
- get(key) 读取key对应的键值
- has(key) 判断某个键是否在当前Map对象之中,返回布尔值
- delete(key) 删除某个键,返回布尔值
- clear() 清除所有成员,无返回值
遍历方法
三个遍历器生成函数和一个遍历方法
Map的遍历顺序是插入顺序
- keys() 返回键名的遍历器
- values() 返回键值的遍历器
- entries() 返回所有成员的遍历器
- forEach() 使用回调函数遍历每个成员
Map结构的默认遍历器接口(Symbol.iterator属性),就是entries()
map[Symbol.iterator] === map.entries //true
WeakMap
WeakMap与Map结构类似,也是用于生成键值对的集合。
与Map的区别
- WeakMap只接受对象作为键名(null除外)
- WeakMap的键名所执行的对象,不计入垃圾回收机制
使用场景
想往对象上添加数据,但又不想干扰垃圾回收机制,就可以使用WeakMap。WeakMap的专用创和就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏。
注意点
WeakMap弱引用的只是键名,不是键值,键值依然是正常引用。
键值obj是正常引用,所以,即使在WeakMap外部消除了obj的引用,WeakMap内部的引用依然存在。
let wm = new WeakMap();
let key = {};
let obj = {foo: 1};
wm.set(key, obj);
obj = null;
console.log(wm.get(key)); //{foo: 1}
属性和方法
- WeakMap没有遍历操作,也没有size属性。
- 无法清空,不支持clear方法
方法
- set(key,value)
- get(key)
- has(key)
- delete(key)
用途
部署私有属性
Countdown类的两个内部属性_counter和_action,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏。
const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
constructor(counter, action) {
_counter.set(this, counter);
_action.set(this, action);
}
dec() {
let counter = _counter.get(this);
if (counter < 1) return;
counter--;
_counter.set(this, counter);
if (counter === 0) {
_action.get(this)();
}
}
}
const c = new Countdown(2, () => console.log('DONE'));
c.dec()
c.dec()
// DONE