1. Set
概念:Set 对象是值的集合,可以按照值添加的顺序来它的元素。允许存储任何类型的唯一值。Set 中相同的值只会出现一次。
其中,判定值相同的方法是使用'==='来判断。
注意,当Set中已经存在一个 NaN 元素之后,再往 Set 中存储 NaN 时,会判定 Set 已经存在 NaN 了(尽管 NaN !== NaN )。
1-1. Set 基本使用
// 创建一个Set实例:
const set1 = new Set()
// 向Set实例中添加元素
set1.add(1)
set1.add(2)
set1.add(2)
set1.add({name: 'zhangsan', age: 20})
set1.add({name: 'zhangsan', age: 20})
const numObj = { num1: 1, num2: 2, num3: 3 }
set1.add(numObj)
set1.add(numObj)
console.log(set1)
// {
// 1,
// 2,
// { name: 'zhangsan', age: 20 },
// { name: 'zhangsan', age: 20 },
// { num1: 1, num2: 2, num3: 3 }
// }
1-2. Set 常用方法
Set.prototype.size:返回Set对象中的值的个数Set.prototype.add(value):在Set对象尾部添加一个元素。返回该Set对象。Set.prototype.delete(value):移除值为value的元素,并返回一个布尔值来表示是否移除成功。Set.prototype.entries():返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值的数组。为了使这个方法和Map对象保持相似, 每个值的键和值相等。Set.prototype.has(value):返回一个布尔值,表示该值在Set中存在与否。Set.prototype.keys():与values()方法相同,返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。Set.prototype.values():返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。Set.prototype.clear():移除Set对象内的所有元素。
例:
const set1 = new Set()
set1.add(1)
set1.add(2)
set1.add('123')
set1.add(null)
set1.add(void 0)
set1.add(NaN)
set1.add(NaN)
console.log(set1) // { 1, 2, '123', null, undefined, NaN }
console.log(set1.size) // 6
console.log(set1.delete(NaN)) // true
console.log(set1.delete(3)) // false
console.log(set1) // { 1, 2, '123', null, undefined }
console.log(set1.entries())
// {
// [ 1, 1 ],
// [ 2, 2 ],
// [ '123', '123' ],
// [ null, null ],
// [ undefined, undefined ]
// }
console.log(set1.has(void 0)) // true
console.log(set1.has(3)) // false
console.log(set1.keys()) // { 1, 2, '123', null, undefined }
console.log(set1.values()) // { 1, 2, '123', null, undefined }
console.log(set1.clear()) // undefined
console.log(set1) // {}
1-3. 迭代 Set 对象
const setObj = new Set()
setObj.add(1)
setObj.add('a')
setObj.add({age: 20})
for(let item of setObj) {
console.log(item)
}
// 1
// a
// { age: 20 }
1-4. 数组去重
let arr = [1,2,2,2,3]
let setArr = new Set(arr)
let arr1 = Array.from(setArr)
console.log(arr) // [ 1, 2, 2, 2, 3 ]
console.log(setArr) // Set(3) { 1, 2, 3 }
console.log(arr1) // [ 1, 2, 3 ]
或者:
let arr = [1,2,2,2,3]
let arr3 = [...new Set(arr)]
console.log(arr3) // [ 1, 2, 3 ]
2. Map
Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者基本类型)都可以作为一个键或一个值。
- 键的比较基于零值相等算法。
- [
NaN] 是与NaN相等的(虽然NaN !== NaN),剩下所有其它的值是根据===运算符的结果判断是否相等。 - 在目前的 ECMAScript 规范中,
-0和+0被认为是相等的,
2-1. Map 基本使用
// 创建 Map 实例
const map1 = new Map()
console.log(map1)
// 添加实例:map.Set(key, value)
map1.set('name', 'zhangsan')
map1.set('age', '20')
console.log(map1) // {'name' => 'zhangsan', 'age' => '20'}
2-2. Map 常用方法
Map.prototype.size:返回Map对象中的键值对数量。Map.prototype.clear():移除Map对象中所有的键值对。Map.prototype.delete(key):移除Map对象中指定的键值对,如果键值对存在并成功被移除,返回true,否则返回false。Map.prototype.get(key):返回与key关联的值,若不存在关联的值,则返回undefined。Map.prototype.has(key):返回一个布尔值,用来表明Map对象中是否存在与key关联的值。Map.prototype.keys():返回一个新的迭代对象,其中包含Map对象中所有的键,并以插入Map对象的顺序排列。Map.prototype.values():返回一个新的迭代对象,其中包含Map对象中所有的值,并以插入Map对象的顺序排列。Map.prototype.entries():返回一个新的迭代对象,其为一个包含Map对象中所有键值对的[key, value]数组,并以插入Map对象的顺序排列。
例:
const map2 = new Map()
// console.log(map2)
// 添加实例:map.Set(key, value)
map2.set('name', 'zhangsan')
map2.set('age', '20')
const numObj = { num1: 1, num2: 2 }
const numObjValue = { numObjValue1: 'vlue1', numObjValue2: 'vlue2' }
map2.set(numObj, numObjValue)
console.log(map2)
// {
// 'name' => 'zhangsan',
// 'age' => '20',
// { num1: 1, num2: 2 } => { numObjValue1: 'vlue1', numObjValue2: 'vlue2' }
// }
console.log(map2.size) // 3
console.log(map2.get(numObj)) // { numObjValue1: 'vlue1', numObjValue2: 'vlue2' }
console.log(map2.has(numObj)) // true
console.log(map2.has({ num1: 1, num2: 2 })) // false
console.log(map2.keys()) // [Map Iterator] { 'name', 'age', { num1: 1, num2: 2 } }
console.log(map2.values()) // [Map Iterator] { 'zhangsan', '20', { numObjValue1: 'vlue1', numObjValue2: 'vlue2' } }
console.log(map2.entries())
// {
// [ 'name', 'zhangsan' ],
// [ 'age', '20' ],
// [
// { num1: 1, num2: 2 },
// { numObjValue1: 'vlue1', numObjValue2: 'vlue2' }
// ]
// }
console.log(map2.delete(numObj)) // true
console.log(map2) // { 'name' => 'zhangsan', 'age' => '20' }
console.log(map2.clear()) // undefined
console.log(map2) // {}
2-3. NaN 和 undefined 作为 Map 对象的键
const myMap = new Map()
myMap.set(NaN, 'not a number')
console.log(myMap.get(NaN)) // "not a number"
myMap.set(undefined, 'key is undefined')
console.log(myMap.get(undefined)) // key is undefined
2-4. 迭代 Map 对象
const mapObj = new Map()
mapObj.set(NaN, 'not a number')
mapObj.set(undefined, 'key is undefined')
mapObj.set({name: 'zhangsan'}, {age: 20})
// 使用 for-of 迭代 Map 对象
for(item of mapObj) {
console.log(item)
}
// [ NaN, 'not a number' ]
// [ undefined, 'key is undefined' ]
// [ { name: 'zhangsan' }, { age: 20 } ]
2-5. Map 与数组的关系
const kvArray = [["key1", "value1"], ["key2", "value2"]];
// 使用常规的 Map 构造函数可以将一个二维键值对数组转换成一个 Map 对象
const myMap = new Map(kvArray);
console.log(myMap.get("key1")); // "value1"
// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维键值对数组
console.log(Array.from(myMap)); // 输出和 kvArray 相同的数组:[ [ 'key1', 'value1' ], [ 'key2', 'value2' ] ]
// 更简洁的方法来做如上同样的事情,使用展开运算符
console.log([...myMap]); // [ [ 'key1', 'value1' ], [ 'key2', 'value2' ] ]
// 或者在键或者值的迭代器上使用 Array.from,进而得到只含有键或者值的数组
console.log(Array.from(myMap.keys())); // 输出 ["key1", "key2"]
3. WeakMap
WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。
3-1. WeakMap 基本使用
// 创建 WeakMap 实例
const weakMapObj = new WeakMap()
const objKey1 = { name: 'num' }
weakMapObj.set(objKey1, { num1: 1, num2: 2, num3: 3 })
function fun() {}
weakMapObj.set(fun, 'emptyFun')
console.log(weakMapObj) // 结果见下图
// key 必须是对象类型的数据
weakMapObj.set('colors', ['red', 'green', 'blue']) // 报错:Invalid value used as weak map key
3-2. WeakMap 常用方法
WeakMap.prototype.get(key):返回WeakMap中与key相关联的值,如果key不存在则返回undefined。WeakMap.prototype.has(key):返回一个布尔值,断言一个值是否已经与WeakMap对象中的key关联。WeakMap.prototype.delete(key):删除WeakMap中与key相关联的值。删除之后,WeakMap.prototype.has(key)将会返回false。
// 创建 WeakMap 实例
const weakMapObj = new WeakMap()
const objKey1 = { name: 'num' }
weakMapObj.set(objKey1, { num1: 1, num2: 2, num3: 3 })
function fun() {}
weakMapObj.set(fun, 'emptyFun')
console.log(weakMapObj.get(fun)) // 'emptyFun'
console.log(weakMapObj.has(objKey1)) // true
console.log(weakMapObj.delete(fun)) // true
console.log(weakMapObj) // 结果见下图
3-3 注意:
Map 与 WeakMap 相比,
Map 的缺点是:
可能会导致内存泄漏,因为数组会一直引用着每个键和值。这种引用使得垃圾回收算法不能回收处理他们,即使没有其他任何引用存在了。
WeakMap 的缺点是:
由于 WeakMap 持有的是每个键对象的“弱引用, 所以WeakMap 的 key 是不可枚举的(没有方法能给出所有的 key)。如果 key 是可枚举的话,其列表将会受垃圾回收机制的影响,从而得到不确定的结果。因此,如果你想要这种类型对象的 key 值的列表,你应该使用 Map。