Open madfour opened 3 years ago
Set
对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set
Set
本身是构造函数,用来生成set数据结构。
new Set([iterable])
Set
函数可以接受一个数组(或者是具有iterable(可迭代的)接口的其它数据结构)作为参数,用来初始化。
// 示例:接受数组作为参数
let set = new Set([1, 2, 3, 3, 3, 4, 5])
set.size // 5
[...set] // [1,2,3,4,5]
// 示例2
let set = new Set(document.querySelectorAll('div'))
去除数组重复的方法
[...new Set(array)]
// 去除字符串里重复字符
[...new Set('adsaddbcd')].join('') // "adsbc"
Array.form
方法可以将Set
结构转为数组,(可用于数组去重)
new Set([1,2,3,4,5]) // {1, 2, 3, 4, 5}
Array.from(new Set([1,2,3,4,3,4,5])) // [1, 2, 3, 4, 5]
Set
中的特殊值
向Set
加入值时,不会发生类型转换,所以5 和 '5'
是不同的,Set
内部判断两个值是否不同,类似于===
。主要的区别是向 Set 加入值时认为NaN等于自身,而精确相等运算符认为NaN不等于自身。
+0
与-0
在存储判断唯一性的时候是恒等的,所以不重复。undefined
与undefined
是恒等的,所以不重复。NaN
与NaN
时不恒等的,但在Set
中认为NaN和NaN相等,所以只能存在一个,不重复。属性
size
:返回Set
实例的成员总数。方法
操作方法
add(value)
: 添加某个值,返回Set
结构本身(可以链式调用)
delete(value)
: 删除某值,返回一个布尔值,删除成功返回true,否则返回false
has(value)
: 返回一个布尔值,表示该值是否为Set
的成员
clear()
: 清除所有成员,没有返回值。
let set = new Set(['a', 'a', 'b', 1, 2, 1])
set.add('c').add({'a': 1}) // Set {"a", "b", 1, 2, "c", {a: 1}}
set.size // 6
set.has(2) // true
遍历方法, (Set
的遍历顺序是插入顺序)
key()
:返回键名的遍历器
values()
:返回键值的遍历器
entries()
:返回键值对的遍历器
forEach()
:使用回调函数遍历每个成员
由于
Set
结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys
方法和values
方法的行为完全一致。
let set = new Set(['a', 'b', 1, 2])
set.keys() // {"a", "b", 1, 2} 和set.values(),set.entries()的打印结果一样
// 由于键值键名同值,keys和values行为一致
for(let item of set.keys()){
console.log(item) // 依次为 a, b, 1, 2
}
for(let item of set.entries()){
console.log(item) // 依次为 ['a','a'], ['b','b'], [1,1], [2,2]
}
set.forEach((value, key) => {
console.log(value,key) // a,a b,b 1,1 2,2
})
Set
可默认遍历,它的默认遍历器生成函数就是它的values
方法。
Set.prototype[Symbol.iterator] === Set.prototype.values
即,可省略values
方法,直接遍历set
实例,等同于遍历set
实例的values
方法
for(let item of set){
console.log(item) // 依次为 a, b, 1, 2
}
Set
对象作用
数组去重(利用扩展运算符):[...new Set([1,1,2])]
数组的map
和filter
方法可间接作用Set
,所以可容易实现并集、交集、差集
// 并集(合并两个`Set`对象)
let a = new Set([1,2,3])
let b = new Set([2,3,4])
new Set([...a, ...b]) // Set {1, 2, 3, 4}
// 交集 let intersect = new Set([...a].filter(x => b.has(x))); // Set {2, 3}
// 差集 (a相对b的差集) let diff = new Set([...a].filter(x => !b.has(x))); // Set {1}
WeakSet 对象允许你将弱引用对象储存在一个集合中。(弱引用的Set)
WeakSet和Set的区别:
和Set
类似,但WeakSet
仅接受对象作为value,而不能有其它类型的值。
如果对象本身没有其他的引用了(不包括WeakSet),那么自动从WeakSet删除。
WeakSet
对象中储存的对象值都是被弱引用的,即垃圾回收机制不考虑 WeakSet 对该对象的应用,如果没有其他的变量或属性引用这个对象值,则这个对象将会被垃圾回收掉(不考虑该对象还存在于 WeakSet 中)。所以,WeakSet 对象里有多少个成员元素,取决于垃圾回收机制有没有运行,运行前后成员个数可能不一致,遍历结束之后,有的成员可能取不到了(被垃圾回收了),WeakSet 对象是无法被遍历的(ES6 规定 WeakSet 不可遍历),也没有办法拿到它包含的所有元素
属性与方法:
属性:new WeakSet() 构造函数,任何一个具有 Iterable 接口的对象,都可以作参数
方法:支持add(value)
、has(value)
、delete(value)
。但不支持 size
和 keys()
,并且不可迭代。
let jack = {name: "jack"};
let weakSet = new WeakSet();
weakSet.add(jack, 22);
console.log( weakSet.has(jack) ); // true
weakSet.delete(jack);
console.log( weakSet ); // WeakSet {}
Map
对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。构造函数Map可以接受一个数组作为参数。
Map
和Set
区别:
Set
以[value, value]的形式储存元素,Map
是以 [key, value] 的形式储存Map
任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构,都可以当作Map构造函数的参数。也就是说,
Set
和Map
都可以用来生成新的Map
。const set = new Set([ ['foo', 1], ['bar', 2] ]); const m1 = new Map(set); m1.get('foo') // 1
const m2 = new Map([['baz', 3]]); const m3 = new Map(m2); m3.get('baz') // 3
如果读取一个未知的键,则返回`undefined`
```javascript
new Map().get('asdfsdgas') // undefined
注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。这一点要非常小心。
const map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined
上面代码的set和get方法,表面是针对同一个键,但实际上这是两个值,内存地址是不一样的,因此get方法无法读取该键,返回undefined。
由上可知,Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。
这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。
如果 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键。(这点和Set
相同)比如:
比如0和-0就是一个键,
布尔值true
和字符串true
则是两个不同的键。
undefined
和null
也是两个不同的键。
虽然NaN不严格相等于自身,但 Map
将其视为同一个键。
let map = new Map();
map.set(-0, 123);
map.get(+0) // 123
map.set(true, 1);
map.set('true', 2);
map.get(true) // 1
map.set(undefined, 3);
map.set(null, 4);
map.get(undefined) // 3
map.set(NaN, 123);
map.get(NaN) // 123
属性
size
:返回Map
实例所包含的元素个数。方法
操作方法
set(key, value)
: set
方法设置键名key
对应的键值为value
,返回整个 Map 结构。如果key
已经有值,则键值会被更新,否则就新生成该键。set
方法返回的是当前的Map对象,因此可以采用链式写法。
get(key)
: 读取key
对应的键值,如果找不到key
,返回undefined
。
has(key)
: 返回一个布尔值,表示某个键是否在当前 Map 对象之中。
delete(key)
: 删除某个键,返回true。如果删除失败,返回false。
clear()
: 清除所有成员,没有返回值。
const m = new Map()
m.set('a', 123) // Map(1) {"a" => 123} 键是字符串
let m2 = new Map().set('a', 123).set(2, 'b') // 链式写法
m.get('a') // 123
m.has('a') // true
遍历方法,(遍历顺序是插入顺序)
keys()
:返回键名的遍历器。
values()
:返回键值的遍历器。
entries()
:返回所有成员的遍历器。
forEach()
:遍历 Map 的所有成员。
let mp = new Map([['a', 'a1'], ['b', 'b1'], ['c', 'c1']])
mp.keys() // MapIterator {"a", "b", "c"}
mp.values() // MapIterator {"a1", "b1", "c1"}
mp.entries() // MapIterator {"a" => "a1", "b" => "b1", "c" => "c1"}
和Set
一样,使用for...of
循环
for(let key of mp.keys()){
console.log(key) // 依次打印 "a", "b", "c"
}
// 等同于使用map.entries()
for (let [key, value] of mp) {
console, log(ley, value) // 依次:a a1, b b1, c c1
}
上面最后的实例,原因:Map结构的默认遍历器接口(Symbol.iterator
属性),就是entries
方法。
map[Symbol.iterator] === map.entries // true
遍历实现
Map使用扩展运算符转为数组,在结合数组的map
和filter
方法。
let mp = new Map([['a', 'a1'], ['b', 'b1'], ['c', 'c1']])
new Map([...mp].filter(([k, v]) => k != 'c')) // Map(2) {"a" => "a1", "b" => "b1"}
new Map([...mp].map(([k, v]) => [k, v+'-m'])) // Map(3) {"a" => "a1-m", "b" => "b1-m", "c" => "c1-m"}
Map 还有一个forEach
方法,与数组的forEach
方法类似,也可以实现遍历。
mp.forEach(function(value, key, map) {
console.log("Key: %s, Value: %s", key, value);
});
// Key: a, Value: a1
// Key: b, Value: b1
// Key: c, Value: c1
可以接受第二个参数,用来绑定this
const reporter = {
report: function(key, value) {
console.log("Key: %s, Value: %s", key, value);
}
};
mp.forEach(function(value, key, map) {
this.report(key, value);
}, reporter);
// console结果和上面一样
上面代码中,forEach
方法的回调函数的this
,就指向reporter
。
与其他数据结构的互相转换
1、Map 转为数组
const map = new Map([[1, 1], [2, 2], [3, 3]])
console.log([...map]) // [[1, 1], [2, 2], [3, 3]]
2、数组 转为 Map
const map = new Map([[1, 1], [2, 2], [3, 3]])
console.log(map) // Map {1 => 1, 2 => 2, 3 => 3}
3、Map 转为对象
因为 Object
的键名都为字符串,而Map 的键名为对象,所以转换的时候会把非字符串键名转换为字符串键名。
function strMapToObj(map) {
let obj = Object.create(null)
for (let [key, value] of map) {
obj[key] = value
}
return obj
}
const map = new Map().set('name', 'An').set('des', 'JS')
strMapToObj(map) // {name: "An", des: "JS"}
4、对象转为 Map
// 写一个转换函数
function objToMap(obj) {
let map = new Map()
for (let key of Object.keys(obj)) {
map.set(key, obj[key])
}
return map
}
objToMap({'name': 'An', 'des': 'JS'}) // Map {"name" => "An", "des" => "JS"}
// 也可使用`Object.entries()`
let obj = {"a":1, "b":2};
let map = new Map(Object.entries(obj));
5、Map 转为 JSON(一般情况二比较多)
// 情况一:Map 的键名有非字符串,选择转为 ‘数组JSON’
function mapToArrayJson(map) {
return JSON.stringify([...map]);
}
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'
// 情况二:Map 的键名都是字符串,选择转为 ‘对象 JSON’
function strMapToJson(strMap) {
return JSON.stringify(strMapToObj(strMap)); // 先将Map转为对象
}
let myMap = new Map().set('yes', true).set('no', false);
strMapToJson(myMap)
// '{"yes":true,"no":false}'
6、JSON 转为 Map
// 正常情况下,所有键名都是字符串
function jsonToStrMap(jsonStr) {
return objToStrMap(JSON.parse(jsonStr));
}
jsonToStrMap('{"yes": true, "no": false}')
// Map {'yes' => true, 'no' => false}
// 特殊情况:整个JSON是一个数组,且每个成员本身又是两个成员的数组,
function jsonToMap(jsonStr) {
return new Map(JSON.parse(jsonStr));
}
jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
// Map {true => 7, Object {foo: 3} => ['abc']}
WeakMap 对象是一组键值对的集合,其中的键是弱引用对象,而值可以是任意。
注意,WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。
WeakMap 中,每个键对自己所引用对象的引用都是弱引用,在没有其他引用和该键引用同一对象,这个对象将会被垃圾回收(相应的key则变成无效的),所以,WeakMap 的 key 是不可枚举的。
属性:
方法:
has(key)
:判断是否有 key
关联对象
get(key)
:返回key
关联对象(没有则则返回 undefined
)
set(key)
:设置一组`key关联对象
delete(key)
:移除 key
的关联对象
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++;
}, false);
WeakMap
与Map
的区别有两点。
WeakMap
只接受对象作为键名(null
除外),不接受其他类型的值作为键名。
WeakMap
的键名所指向的对象,不计入垃圾回收机制。
WeakMap 应用的典型场合就是 DOM 节点作为键名。另一个用处是部署私有属
Map 和 Object 的区别
set、map、weakSet、weakMap 总结
Set
[value, value]
, 键名键值一致(或者说只有键值)。有size属性。add(value)
添加新元素,操作方法还有delete(value)
、has(value)
、clear()
。Map
[key, value]
的形式储存。有size属性。set(key,val)
添加新元素,使用get(key)
获取对应的值。操作方法还有delete(key)
、has(key)
、clear()
。Set 和 Map 的遍历方法:都可使用
keys()
、values()
、entries()
、forEach()
配合for...of
循环遍历。WeakSet
add()
、delete()
、has()
、WeakMap
set()
、get()
、delete()
、has()
、WeakMap 中,每个键对自己所引用对象的引用都是弱引用,在没有其他引用和该键引用同一对象,这个对象将会被垃圾回收(相应的key则变成无效的),所以,WeakMap 的 key 是不可枚举的。