var s = new Set([1,2,3,3,3,4,5])
s.size // 5
[...s] // [1,2,3,4,5]
var ss = new Set([...document.querySelectorAll('div')]);
ss.size // div的个数
// 相当于
[...document.querySelectorAll('div')].forEach(div => ss.add(div))
使用 Set 数组去重 [...new Set(array)]
使用 Set 加入值时不会转换类型 5 和 '5' 是两个不同的值。虽然 NaN === NaN 返回 false, 但是在 Set 内部 NaN 是等于自身的。
var set = new Set();
var a = NaN;
var b = NaN;
set.add(a);
set.add(b);
set.size // 1 Set 内部 两个 NaN 是相等的
但是两个对象总是不等的
var set = new Set();
set.add({});
set.add({});
set.size // 2
属性 方法
1. 属性
Set.prototype.constroctor 构造函数
Set.prototype.size Set 实例的成员总数
2.1 操作方法
add(value) 返回 Set 本身
delete(value) 返回 Booolean
has(value) 返回 Booolean
clear()
Array.from 可以将 Set 结构转为数组
var s = new Set([1,2,3,4]);
var arr = Array.from(s);
arr // [1, 2, 3, 4]
数组去重的另一种方法
var unique = function(arr){
return Array.from(new Set(arr))
}
unique([1,2,2,3,3,4]);
2.2 遍历方法
keys() 返回键名
values() 返回键值
entries() 返回键值对
forEach() 用回调遍历所有成员
Set 遍历的顺序就是插入顺序。如果使用Set保存一个回调函数的列表,调用时就能按照添加的顺序来调用。
1) keys(),values(),entries()
key()、values()、entries()返回的都是遍历器对象。因为 Set 没有键名(或者键名和键值是同一个值),所以key()和value()的行为完全一致。
var set = new Set(['aa', 'bb', 'cc']);
for (var item of set.keys()) {
console.log(item);
}
// aa
// bb
// cc
for (var item of set.values()) {
console.log(item);
}
// aa
// bb
// cc
for (var item of set.entries()) {
console.log(item);
}
// ["aa", "aa"]
// ["bb", "bb"]
// ["cc", "cc"]
var set = new Set(['aa', 'bb', 'cc']);
for(s of set) {
console.log(s);
}
// aa
// bb
// cc
2) forEach()
var set = new Set(['aa', 'bb', 'cc']);
set.forEach((val,key) => console.log(val + ' - sss'))
3) 使用遍历
扩展运算符 (...) 内部使用 for...of 循环,所以也可以是用 Set 结构。
var set = new Set(['a', 'b', 'c']);
var arr = [...set];
// ['a', 'b', 'c']
数组的 mapfilter 方法也可用于 Set
var set = new Set([1, 2, 3]);
set = new Set([...set].map(x => x*2));
// Set {2, 4, 6}
var set = new Set([1, 2, 3, 4, 5]);
set = new Set([...set].filter(x => (x%2) ==0 ));
// Set {2, 4}
Set 很容易实现并集(Union)、交集(Intersect)和差集(Difference)
var a = new Set([1,2,3]);
var b = new Set([2,3,4]);
var union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
var intersect = new Set([...a].filter(x => b.has(x) ));
// Set {2, 3}
var diff = new Set([...a].filter(x => !b.has(x) ));
// Set {1}
var ws = new WeakSet();
ws.add(1); // Uncaught TypeError: Invalid value used in weak set
ws.add(Symbol()); // Uncaught TypeError: Invalid value used in weak set
var a = [[1,2], [3,4]];
var ws = new WeakSet(a);
上面代码中 a 数组的成员成为 WeakSet 的成员,而不是 a 数组本身。因此数组的成员只能是对象。
var map0 = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c');
var map1 = new Map(
[...map0].filter(([k, v]) => k < 3)
);
// 产生Map结构 {1 => 'a', 2 => 'b'}
var map2 = new Map(
[...map0].map(([k, v]) => [k * 2, '_' + v])
);
// 产生Map结构 {2 => '_a', 4 => '_b', 6 => '_c'}
与其他数据结构的互相转换
Map转为数组
使用扩展运算符
var m = new Map().set(true, 7).set({foo: 3}, ['abc']);
[...m]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
数组转为Map
数组直接放入Map的构造函数
var m = new Map([ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]);
m
// Map {true => 7, Object {foo: 3} => ["abc"]}
Map转为对象
所有的键为字符串的Map可转换为数组
function strMapToObj(map) {
let obj = Object.create(null);
for(let [k,v] of map){
obj[k] = v;
}
return obj;
}
var m = new Map().set('a', 'a1a2a3').set('true', true);
strMapToObj(m);
// Object {a: "a1a2a3", true: true}
对象转为Map
function objToStrMap(obj){
let m = new Map();
for(let [k,v] of obj){
m.set(k,v)
}
return m
}
Map转为JSON
Map的键名都是字符串,可以直接转为JSON
function mapToJson(map){
return JSON.stringify(strMapToObj(map));
}
var m = new Map().set('yes', true).set('no', false);
strMapToJson(m)
// '{"yes":true,"no":false}'
Map的键名如果有非字符串,可选择转为数组JSON
function mapToArrayJson(m){
return JSON.stringify([...m])
}
var mm = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(mm)
// "[[true,7],[{"foo":3},["abc"]]]"
WeakMap 的设计目的在于,键名是对象的弱引用(垃圾回收机制不将该引用考虑在内),所以其所对应的对象可能会被自动回收。当对象被回收后,WeakMap 自动移除对应的键值对。典型应用是,一个对应 DOM 元素的WeakMap 结构,当某个 DOM 元素被清除,其所对应的 WeakMap 记录就会自动被移除。基本上,WeakMap 的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏。
var wm = new WeakMap();
var element = document.querySelector(".element");
wm.set(element, "Original");
wm.get(element) // "Original"
element.parentNode.removeChild(element);
element = null;
wm.get(element) // undefined
let myElement = document.getElementById('logo');
let myWeakmap = new WeakMap();
myWeakmap.set(myElement, {timesClicked: 0});
myElement.addEventListener('click', function() {
let logoData = myWeakmap.get(myElement);
logoData.timesClicked++;
myWeakmap.set(myElement, logoData);
}, false);
myElement 是一个 DOM 节点,每当发生 click 事件,就更新一下状态。我们将这个状态作为键值放在 WeakMap 里,对应的键名就是 myElement 。一旦这个 DOM 节点删除,该状态就会自动消失,不存在内存泄漏风险。
WeakMap的另一个用处是部署私有属性。
let _counter = new WeakMap();
let _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)();
}
}
}
let c = new Countdown(2, () => console.log('DONE'));
c.dec()
c.dec()
ES6 笔记四 - Set Map
Set
基本用法
类似数组,但是成员都是唯一的没有重复值。
Set本身是一个构造函数用来生成Set类型的数据
Set可以接受一个数组或者类数组的对象作为参数来初始化
使用 Set 数组去重
[...new Set(array)]
使用 Set 加入值时不会转换类型 5 和 '5' 是两个不同的值。虽然
NaN === NaN
返回 false, 但是在 Set 内部 NaN 是等于自身的。但是两个对象总是不等的
属性 方法
1. 属性
Set.prototype.constroctor
构造函数Set.prototype.size
Set 实例的成员总数2.1 操作方法
Array.from
可以将 Set 结构转为数组数组去重的另一种方法
2.2 遍历方法
Set 遍历的顺序就是插入顺序。如果使用Set保存一个回调函数的列表,调用时就能按照添加的顺序来调用。
1) keys(),values(),entries()
key()、values()、entries()返回的都是遍历器对象。因为 Set 没有键名(或者键名和键值是同一个值),所以key()和value()的行为完全一致。
Set 结构的实例默认可以遍历 它的默认遍历器生成函数就是它的
values
方法Set.prototype[Symbol.iterator] === Set.prototype.values
那么就可以直接用
for...of
来遍历Set。省略 values 方法。2) forEach()
3) 使用遍历
扩展运算符 (...) 内部使用
for...of
循环,所以也可以是用 Set 结构。数组的
map
filter
方法也可用于 SetSet 很容易实现并集(Union)、交集(Intersect)和差集(Difference)
WeakSet
与 Set 区别
如果其他对象不再引用该对象,那 GC 会自动回收该对象占用的内存,不考虑该对象还存在于 WeakSet 中。
那就无法引用 WeakSet 的成员,因此 WeakSet 不可遍历。
上面代码中 a 数组的成员成为 WeakSet 的成员,而不是 a 数组本身。因此数组的成员只能是对象。
WeakSet 没有
size
forEach
属性,没有办法遍历它的成员。WeakSet不能遍历,是因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。WeakSet的一个用处,是储存DOM节点,而不用担心这些节点从文档移除时,会引发内存泄漏。
Map
基本用法
对象(Object) 本质上是键值对的集合(Hash结构), 但是只能用字符串当键(字符串 - 值)。
Map 数据结构中各种类型的值(包括对象)都可以当作键(值 - 值)。
Map也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。
true 和 字符串 'true' 在 Map 表示两个值
重复赋值会覆盖
读取一个未知的键,则返回undefined
new Map().get('abc')
只有对用一个对象的引用 Map 才会视为同一个键
所以说,Map 的键是和内存地址绑定的,内存地址不同,就视为两个键。这样就解决了同名属性的问题。
在 Map 中 NaN 视为同一个键
0
与-0
也是同一个键,因为0 === -0
属性 方法
遍历方法
Map的遍历顺序就是插入顺序。
使用扩展运算符(...)Map结构可转为数组结构
结合数组的map方法、filter方法,可以实现Map的遍历和过滤(Map本身没有map和filter方法)
与其他数据结构的互相转换
Map转为数组
使用扩展运算符
数组转为Map
数组直接放入Map的构造函数
Map转为对象
所有的键为字符串的Map可转换为数组
对象转为Map
Map转为JSON
Map的键名都是字符串,可以直接转为JSON
Map的键名如果有非字符串,可选择转为数组JSON
JSON转为Map
正常情况下所有键名都是字符串
有一种特殊情况 整个 JSON 是一个数组,每个数组成员本身又是一个有两个成员的数组,这时课一一对应的转为Map
WeakMap
WeakMap 与 Map 类似,只接受对象作为键名(null 除外),而且键名所指向的对象,不计入垃圾回收机制。
WeakMap 的设计目的在于,键名是对象的弱引用(垃圾回收机制不将该引用考虑在内),所以其所对应的对象可能会被自动回收。当对象被回收后,WeakMap 自动移除对应的键值对。典型应用是,一个对应 DOM 元素的WeakMap 结构,当某个 DOM 元素被清除,其所对应的 WeakMap 记录就会自动被移除。基本上,WeakMap 的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏。
WeakMap 没有遍历操作,无法清空,只有四个方法可用:get()、set()、has()、delete()
WeakMap 应用的典型场合就是 DOM 节点作为键名。
myElement 是一个 DOM 节点,每当发生 click 事件,就更新一下状态。我们将这个状态作为键值放在 WeakMap 里,对应的键名就是 myElement 。一旦这个 DOM 节点删除,该状态就会自动消失,不存在内存泄漏风险。
WeakMap的另一个用处是部署私有属性。
Countdown 类的两个内部属性 _counter 和 _action ,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏。