Set 类似于数组,但是成员的 值都是唯一的,没用重复的值。Set本身是一个构造函数,用来生成Set数据结构。
const s = new Set();
[2,3,4,5,6,4,2,2,7].forEach(x => s.add(x))
for (let i of s) {
console.log(i)
} // 2 3 4 5 6 7
// Set 不会添加重复的值。
Set 函数可以接受一个数组(或具有 iterable 接口的其他数据结构)作为参数,用来初始化,如下:
// 实例一
const set = new Set([1,2,3,4,5,5])
[...set] // [1,2,3,4,5]
// 实例二
const items = new Set([1,2,3,3,4])
items.size // 4
// 上面代码还展示了数组去重的方法
[...new Set(array)]
// 或者字符串去重
[...new Set('aabbcc')].join('')
向 Set 加入值的时候,不会发生类型转换, 所有5 和 '5'两个不同的值。在Set 内部判断两个值是否不同,使用的算法叫做 'Same-value-zero equality',它和 '==='类似,区别在于 向 Set 加入值的时候认为 NaN等于自身,而 '===' 则认为 NaN 不等于自身
let set = new Set();
let a = NaN;
let b = NaN;
set.add(a);
set.add(b);
set // Set {NaN}
// 如上,只能向Set中添加一个NaN则说明了刚刚的证明
在 Set中两个对象总是不相等的。
let set = new Set();
set.add({})
set.size // 1
set.add({})
set.size // 2
Set 结构的实例有以下属性
Set 实例的方法分为两大类:操作方法和遍历方法
Set.prototype.add(value) // 添加某个值,返回 Set 结构本身 Set.prototype.delete(value) // 删除某个值,返回一个布尔值,表示是否删除成功 Set.prototype.has(value) // 返回一个布尔值,表示该值是否为Set的成员 Set.prototype.clear() // 清除所有成员,没用返回值
s.add(1).add(2).add(2);
// 注意2被加入了两次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
// Object 和 Set 如何判断一个键的区别
// 对象
const p = {
'w': 1,
'h': 2
}
if(p[a]){
no
}
// Set
const p = new Set()
p.add('w')
p.add('h')
if(p.has(c)){
no
}
Array.from 方法可以将 Set 结构转为数组,如下:
const items = new Set([1,2,3,4,5])
const array = Array.from(items)
// 数组去重的另外一种方法
function d(a){
return Array.from(new Set(a))
}
d([1,2,3,3,5])
Set 结构的实例有四个遍历方法,用于遍历成员
注意:Set遍历顺序就是插入顺序,这个特性在特定情况非常有用,比如使用Set保存一个回调函数列表,调用时就能保证按照添加顺序调用。
上面三个都是返回遍历器对象,由于Set结构没用键名,只有键值(或者说键名和键值是同一个值),所以以上方法的行为完全一致
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
注意:Set结构的实例默认可遍历,它的默认遍历器生成函数就是它的 values方法,这样我们就可以省略 values,直接用 for...of循环遍历Set
Set结构的实例和数组一样,也有 forEach方法,用于对每个成员执行某种操作,没用返回值。forEach 可以用第二个参数表示绑定处理函数内部的 this 对象。
扩展运算符(...)内部使用 for...of循环样能用于 Set 结构
let arr = [3, 5, 2, 2, 5, 5];
let unique = [...new Set(arr)];
// [3, 5, 2]用来去重操作
// 实现 并集、交集、差集
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
如果想在遍历操作中同步改变原理的Set结构,只能利用原有的Set结构映射一个新的结构,然后赋值给原来的Set,另一个就是通过 Array.from方法。
// 方法一
let set = new Set([1, 2, 3]);
set = new Set([...set].map(val => val * 2));
// set的值是2, 4, 6
// 方法二
let set = new Set([1, 2, 3]);
set = new Set(Array.from(set, val => val * 2));
// set的值是2, 4, 6
WeakSet 结构与Set类似,也不是重复的值的集合,但是和Set有两个区别,第一个它的成员只能为对象,另一个它的对象都是弱印象,即垃圾回收机制不考虑 WeakSet对该对象的引用,通俗的讲就是,如果该对象没用在其他对象中被引用,那么该对象就会被回收,不会考虑这个对象是否在 WeakSet中。
依赖于垃圾回收进制依赖引用计数,如果一个值的引用次数不为0,那么就不会被回收,但是有的时候,结束使用该值后,会忘记取消引用,就会导致内存无法释放从而导致内存泄漏。但是 WeakSet里面的而引用不会计入垃圾回收机制,所以适合存放临时的对象,一旦外部消失,那么WeakSet里面的引用就会自动消失。
基于以上的特点,WeakSet 成员不适合被引用,所以 WeakSet无法被遍历。
它也是一个构造函数,可以通过 new 来创建
const ws = new WeakSet()
// 做为构造函数,WeakSet
可以接受一个数组或类似数组的对象作为参数,该数组的所有成员,
都会自动成为 WeakSet实例对象的成员。
注意:只能是数组的成员成为WeakSet的成员,而不是 a 数组本身,这就意味着,数组的成员只能是对象。
注意:WeakSet 同样没有size 属性,不能遍历其成员。
JavaScript的对象,本质上是键值对的集合,但是传统上只能字符串当做键,这给他带来了很大的限制。Map的出现,就是让各种类型的值都可以当作键。Map提供的是 “值-值”的对应。
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"
注意:Set 和 Map 都可以用来生成新的Map,如果对同一个键多次赋值,那么前面的将被后面的值覆盖。另外只有对同一个对象的引用,Map结构才将其视为同一个键。另外同样的两个实例,在Map中将被视为两个键。
总结:综上所述,Map的键实际上跟内存地址绑定的,只要内存地址不一样,就视为两个键。这样就可以解决同名属性碰撞的问题。如果我们扩展别人库的时候,如果使用对象最为键名,就不用担心自己的属性与原作者属性冲突。
如果Map的键是一个简单类型的数值,则只要两个值严格相等,Map将其视为一个键,0 和 -0 是一个键,true 和 'true'则是两个不同的键, undefined 和 null 也是两个不同的键, 另外 NaN 在Map 中视为同一个键
size 属性返回Map结构的成员总数
const map = new Map()
map.set('foo', ture)
map.set('bar', false)
map.size // 2
set 方法设置键名 key 对应的键值为 value,然后返回整个 Map 结构。如果 key 已经有值,则键值会被更新,否则就新生成该键。
const m = new Map()
m.set('e', 6) // 键值是字符串
m.set(2, 's') // 键是数值
m.set(undefined, 'n') // 键是 undefined
// set方法返回的是当前的Map对象,因此可以采用链式写法
let map = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c')
get 方法读取 key 对应的键值,如果找不到key就返回 undefined
const m = new Map();
m.set('c', 124)
m.get('c') // 124
返回一个布尔值,用来表示某个键是否在当前 Map 对象中
let map = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c')
map.has(1) // true
map.has(4) // false
delete方法删除某个键,返回 true,如果删除失败,则返回 false
let map = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c')
map.delete(1) // true
map.delete(4) // false
clear 方法清除所有成员,没有返回值
let map = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c')
map.size // 3
map.clear()
map.size // 0
注意:Map的遍历顺序就是插入顺序。
Map 结构转为数组结构,比较快速的方法是使用扩展运算符(...),另外Map可以通过 forEach 可以实现遍历。
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
[...map.keys()]
// [1, 2, 3]
[...map.values()]
// ['one', 'two', 'three']
[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]
[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]
通过扩展运算符(...)
将数组 传入 Map构造函数,就可以转为Map
new Map([
[true, 7],
[{foo: 3}, ['abc']]
])
如果Map的键都是字符串,它可以无损地转为对象,如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。
Map转为JSON要区分两种情况。一种情况是,Map 的键名都是字符串,这时可以选择转为对象JSON。另外一种情况是,Map 的键名有非字符串,这时可以选择转为数组JSON
JSON转为Map,正常情况下,所有键名都是字符串。但是,有一种特殊情况,整个JSON就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为Map.
WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合
WeakMap 和 Map 的区别
1. WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
2. WeakMap的键名所指向的对象,不计入垃圾回收机制。
如果 我们想在某个对象上面存放以未数据,但是会形成对于这个对象的引用,如果我们不需要这两个对象,就必须手动删除,否则垃圾回收机制就不会释放占用的内存。
WeakMap 就是为了解决这个问题而诞生,它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用被清除,垃圾回收机制就会释放该对象所占用的内存,也就是说一旦不再需要,WeakMap里面的键名对象和所对应的键值对会自动消失,不用手动删除。
注意:WeakMap弱引用的只是键名,而不是键值。键值依然是正常引用。
WeakMap 与 Map 在 API的区别主要有两个,一是没有遍历操作(没有keys,values,entries),也没有size属性。因为没有办法列出所有键名,某个键名是否存在完全不可预测,跟垃圾回收机制是否运行相关。这一刻可以取到键名,下一个垃圾回收机制突然运行了,这个键名就没有了。为了防止不确定性,就统一规定不能取到键名。二是无法情况,即不支持 clear 方法。因此 WeakMap 有四个方法:get()、set()、has()、delete()
const wm = new WeakMap();
// size、forEach、clear 方法都不存在
wm.size // undefined
wm.forEach // undefined
wm.clear // undefined
ES6入门系列