guyuezhai / interviewSummary

some summary of interview
0 stars 0 forks source link

介绍下 Set、Map、WeakSet 和 WeakMap 的区别? #36

Closed guyuezhai closed 4 years ago

guyuezhai commented 4 years ago

Set 和 Map 主要应用场景在于 数据重组和数据存储 Set 是一种集合的数据结构,Map 是一种字典的数据结构

1. 集合 Set

ES6 新增的一种数据结构,类似于数组,数据成员唯一且无序、不重复

Set 本身是一种构造函数,用来生成Set数据结构

const s = new Set([1,2,3,4,1,2,3,4,2,2,2,3,3,4])
for(let i of s){
  console.log(i) // 1、2、3、4
}
// 去除重复的数组对象
[...s] // [1, 2, 3, 4]  Set 转 Array

Set 对象允许存储任何类型的唯一值,无论是原始值或者是对象引用

向Set添加值时不会发生类型转换,所以number 3 和 string '3' 是两个不同的值,Set 内部会判断是否两个值相同,使用"Same-value-zero equality"的算法,类似于精确相等运算符(===), 主要的区别是在Set中会把两个为NaN的值当做相等来处理,而在js中会认为NaN与任何值都是不相等的

let s = new Set();
let a = NaN;
let b= NaN;
s.add(a);
s.add(b);
console.log(s,a==b) // Set(1) {NaN} false

let s1= new Set();
s1.add(3);
s1.add('3');
console.log([...s1]) // [3, "3"]

// 利用 Array.from() console.log(Array.from(set)) // [1, 2, 3, 4, 5]

遍历的方法(遍历的顺序为插入的顺序)
  - keys():  返回一个包含集合中所有键的迭代器
  - values(): 返回一个包含集合中所有值的迭代器
  - entries(): 返回一个包含Set对象中所有元素键值对的迭代器
  - forEach(callbackFn,thisArg): 用于对集合成员执行callbackFn操作,如果有thisArg参数,回调函数中的this会是这个参数,没有返回值
```js
let set = new Set([1,2,3,4,5])
let keys=set.keys();
let entries=set.entries();
console.log(keys) //SetIterator {1, 2, 3, 4, 5}
console.log(set.values()) //SetIterator {1, 2, 3, 4, 5}
console.log(entries) //SetIterator {1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5}

for(let item of keys){
   console.log(item) // 1  2  3  4  5
}
for(let item of entries){
   console.log(item) // [1,1]  [2,2]  [3,3]  [4,4]  [5,5]  
}
set.forEach((value,key)=>{
  console.log(key+":"+value) // 1:1  2:2  3:3  4:4  5:5  
})

Set 可默认遍历,默认迭代器生成函数是 values()

Set.prototype[Symbol.iterator]===Set.prototype.values // true

所以 Set 可以使用map,filter方法

let set = new Set([1,2,3,4,5])
set=new Set([...set].map(item=>item*item))
console.log([...set]) //[1, 4, 9, 16, 25]

set=new Set([...set].filter(item=>item>10))
console.log([...set]) //[16, 25]

因此使用 Set 很容易实现交集(Intersect)、并集(Union)、差集(Difference)

let set1 = new Set([1,2,3]);
let set2 = new Set([0,2,4]);
let intersect= new Set([...set1].filter(item=>set2.has(item)))
let union= new Set([...set1,...set2])
let difference= new Set([...set1].filter(item=>!set2.has(item)))

console.log(intersect) //Set(1) {2}
console.log(union) //Set(5) {1,2,3,0,4}
console.log(difference) //Set(2) {1,3}
guyuezhai commented 4 years ago

WeakSet

WeakSet对象允许你将弱引用对象存储在集合中

WeakSet与Set的区别

ws.add(window) ws.add(obj)

ws.has(window) //true ws.has(fool) //false

ws.delete(window) // true ws.has(window) // false

guyuezhai commented 4 years ago

字典(Map)

集合与字典的区别

mp.has(o) //true mp.delete(o) //true mp.has(o) //false

任何具有Iterator接口、且每个成员都是一个双元素的数组的数据结构都可以当做Map构造函数的参数
```js
const set=new Set([['foo',1],['bar',2]])
const mp=new Map(set)
mp.get('foo') //1

const mp2=new Map(['baz',3]);
const mp3=new Map(mp2)
mp3.get('baz') // 3

如果读取一个未知的键,则返回undefined

new Map().get('abss') // undefined

只有对同一个对象的引用,Map结构才将其视为同一个键

const map=new Map()
map.set(['a'],'a')
map.get(['a']) //undefined

上面的代码的setget方法,表面是针对同一个键,但实际上这是两个不同的值,内存地址是不一样的,因此get方法无法读取改键,返回undefined

由上可知,Map的键实际是内存地址的绑定,只要内存地址不一样,就视为两个不同的键。这就解决了同名属性碰撞的(clash)的问题。 如果Map的键是一个简单类型的值(Boolen,number,string),则只要两个值严格想等,Map将视其为同一个键,比如0-0就是一个键,布尔值true和字符串true则是两个不相同的键,另外undefinednull也是两个不同的键,虽然NaN不严格等于自身,但是Map将其视为同一个键

let mp = new Map()
mp.set(-0,123)
mp.get(+0) // 123

mp.set(true,1)
mp.get('true') //undefined
mp.get(true) //1

mp.set(undefined,3)
mp.get(null) //undefined
mp.get(undefined) //3 

mp.set(NaN,123)
mp.get(NaN) //123

Map 的属性和方法

遍历方法

Map 结构的默认遍历器接口(Symbol.iterator属性),就是`entries`方法
```js
map[Symbol.iterator]===map.entries //true

Map 结构转换成数组结构,比较快速的方法是使用扩展运算符(...) 对于forEach

const loger={
    log:function(key,value){
        console.log("Key:%s, Value: %s",key,value)
    }
}
let map=new Map([['name','TOM'],['age',20]])
map.forEach(function(value,key){
    this.log(key,value)
},loger)

//Key:name, Value: TOM
//Key:age, Value: 20

这个例子中,forEach 方法的回调函数的this指向的是loger

与其它数据结构的相互转换

  1. Map转换成Array
    let map=new Map([['name','TOM'],['age',20]])
    console.log([...map]) // [Array(2), Array(2)]
  2. Array 转换成Map
    let map=new Map([['name','TOM'],['age',20]])
    console.log(map) // Map(2) {"name" => "TOM", "age" => 20}
  3. Map 转换成Object

    因为Object的键名都是字符串,而Map的键名为对象,所以转换的时候会把非字符串键名转换成字符串键名

    function mapToObj(map){
    let obj=Object.create(null)
    for(let [key,value] of map){
        obj[key]=value
    }
    return obj
    }
    let map=new Map().set('name','Tom').set('age',20)
    mapToObj(map) // {name: "Tom", age: 20}
  4. Object 转 Map
    function objToMap(obj){
    let map=new Map()
    for(let key of Object.keys(obj)){
        map.set(key,obj[key])
    }
    return map
    }
    objToMap({name: "Tom", age: 20}) // Map(2) {"name" => "Tom", "age" => 20}
  5. Map 转 Json
    function mapToJson(map){
    return JSON.stringify([...map])
    }
    let map=new Map().set('name','Tom').set('age',20)
    mapToJson(map) // "[["name","Tom"],["age",20]]"
  6. Json 转 Map
    function jsonToMap(json){
    return objToMap(JSON.parse(json))
    }
    jsonToMap('{"name": "Tom", "age": 20}') //Map(2) {"name" => "Tom", "age" => 20}
guyuezhai commented 4 years ago

WeakMap

WeakMap 对象是一组键值对的集合,其中的键是弱引用对象,而值可以是任意

WeakMap弱引用的只是键名,而不是键值,键值依然正常引用

WeakMap中,每个键对自己所引用对象的引用都是弱引用,在没有其他引用和该键引用同一个对象的情况下,这个对象将会被垃圾回收(相应的key则会变成无效的),所以,WeakMap的key是不可枚举的

属性:

方法: