卖坚果的怪叔叔 卖坚果的怪叔叔
  • 首页
  • 生活
  • 技术
  • 记录
  • 工具箱
  • 关于我
首页 › 技术 › ES6入门之Set 和 Map

ES6入门之Set 和 Map

坚果大叔
2019-08-04 13:27:35技术阅读 3,057

ES6入门之Set 和 Map-卖坚果的怪叔叔

1.1 基本用法

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

1.2 Set 实例的属性和方法

Set 结构的实例有以下属性

  • Set.prototype.constructor // 构造函数,默认是Set函数
  • Set.prototype.size // 返回 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])

1.3 遍历操作

Set 结构的实例有四个遍历方法,用于遍历成员

  • Set.prototype.keys() // 返回键名的遍历器
  • Set.prototype.values() // 返回键值的遍历器
  • Set.prototype.entries() // 返回键值对的遍历器
  • Set.prototype.forEach() // 使用回调函数遍历每个成员

注意:Set遍历顺序就是插入顺序,这个特性在特定情况非常有用,比如使用Set保存一个回调函数列表,调用时就能保证按照添加顺序调用。

1.3.1 keys()、values、entries()

上面三个都是返回遍历器对象,由于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

1.3.2 forEach()

Set结构的实例和数组一样,也有 forEach方法,用于对每个成员执行某种操作,没用返回值。forEach 可以用第二个参数表示绑定处理函数内部的 this 对象。

1.3.3 遍历的应用

扩展运算符(...)内部使用 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无法被遍历。

2.1 语法

它也是一个构造函数,可以通过 new 来创建

const ws = new WeakSet()

// 做为构造函数,WeakSet

可以接受一个数组或类似数组的对象作为参数,该数组的所有成员,
都会自动成为 WeakSet实例对象的成员。

注意:只能是数组的成员成为WeakSet的成员,而不是 a 数组本身,这就意味着,数组的成员只能是对象。

2.2 WeakSet的方法

  1. WeakSet.prototype.add(value): 向 WeakSet实例添加一个新成员。
  2. WeakSet.prototype.delete(value): 清除 WeakSet实例的指定成员。
  3. WeakSet.prototype.has(value): 返回一个布尔值,表示某个值是否存在 WeakSet实例中。

注意: 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 中视为同一个键

3.1 Map的属性和操作方法

1. size 属性

size 属性返回Map结构的成员总数

const map = new Map()
map.set('foo', ture)
map.set('bar', false)

map.size // 2
2. Map.prototype.set(key, value)

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')
3. Map.prototype.get(key)

get 方法读取 key 对应的键值,如果找不到key就返回 undefined

const m = new Map();

m.set('c',  124)
m.get('c')  // 124
4. Map.prototype.has(key)

返回一个布尔值,用来表示某个键是否在当前 Map 对象中

let map = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c')

map.has(1)  // true
map.has(4)  // false
5. Map.prototype.delete(key)

delete方法删除某个键,返回 true,如果删除失败,则返回 false

let map = new Map()
    .set(1, 'a')
    .set(2, 'b')
    .set(3, 'c')

map.delete(1)   // true
map.delete(4)   // false
6. Map.prototype.clear()

clear 方法清除所有成员,没有返回值

let map = new Map()
    .set(1, 'a')
    .set(2, 'b')
    .set(3, 'c')

map.size    // 3
map.clear() 
map.size    // 0

3.2 遍历方法

  1. Map.prototype.keys(): 返回键名的遍历器
  2. Map.prototype.values(): 返回键值的遍历器
  3. Map.prototype.entries(): 返回所有成员的遍历器
  4. Map.prototype.forEach(): 遍历Map的所有成员

注意: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']]

3.3 与其他数据结构的互相转换

1. Map转为数组

通过扩展运算符(...)

2. 数组转为Map

将数组 传入 Map构造函数,就可以转为Map

new Map([
  [true, 7],
  [{foo: 3}, ['abc']]
])
3. Map 转为对象

如果Map的键都是字符串,它可以无损地转为对象,如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。

4. 对象转为 Map
5. Map 转为 JSON

Map转为JSON要区分两种情况。一种情况是,Map 的键名都是字符串,这时可以选择转为对象JSON。另外一种情况是,Map 的键名有非字符串,这时可以选择转为数组JSON

6. JSON 转为 Map

JSON转为Map,正常情况下,所有键名都是字符串。但是,有一种特殊情况,整个JSON就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为Map.

WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合

WeakMap 和 Map 的区别

1. WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
2. WeakMap的键名所指向的对象,不计入垃圾回收机制。

如果 我们想在某个对象上面存放以未数据,但是会形成对于这个对象的引用,如果我们不需要这两个对象,就必须手动删除,否则垃圾回收机制就不会释放占用的内存。

WeakMap 就是为了解决这个问题而诞生,它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用被清除,垃圾回收机制就会释放该对象所占用的内存,也就是说一旦不再需要,WeakMap里面的键名对象和所对应的键值对会自动消失,不用手动删除。

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

4.1 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

4.2 WeakMap的用途

  1. 应用于 DOM 节点作为键名
  2. WeakMap 的另一个用处是部署私有属性
  3. 就是防止内存泄漏的风险

ES6入门之Set 和 Map-卖坚果的怪叔叔

欢迎关注 公众号【执行上下文】

map set
赞赏 赞(0)
本文系作者 @坚果大叔 原创发布在 卖坚果的怪叔叔。未经许可,禁止转载。
周末小记
上一篇
前端常用的正则表达式
下一篇
在小程序中查看

这是个😊,挣点服务器费用😜

热门文章
病来如山倒!
Mac 修改应用图标小Tips~
Css Filter
什么是 Ajax? 如何创建一个Ajax?
WordPress博客评论微信通知功能!(更新版)
Input 空格问题
专题标签
agent (1) CSS (15) dom (1) Es6入门系列 (14) Git (3) html (1) iview (1) JavaScript (9) js (4) Lif (1) Node (4) pyecharts (1) Python (10) ref (1) scriptable (1) Skill (1) vsCode (1) vue (4) wordpress (2) 三月 (1) 上海 (1) 假期 (1) 其他分享 (3) 前端两三问 (15) 古镇 (1) 外滩 (1) 对象 (1) 小程序 (5) 年度总结 (6) 年终总结 (1) 微信机器人 (4) 打字机 (1) 技术 (1) 日常问题 (8) 日常问题记录 (1) 春天 (2) 服务器 (2) 海族馆 (1) 生活 (8) 网上国网 (1) 记录 (3) 通勤 (1) 重学JavaScript (11) 面试题 (10) 🌸 (1)
什么是Agent?
2026-08-15 23:49:12
499 0 2
什么是 Skill?
2026-08-12 21:32:42
424 1 0
失业在家,我给小朋友做了一个练字小程序!
2026-08-07 16:21:52
848 10 1
上线8年,15万用户的小程序,一共赚了多少钱?
2026-05-30 18:01:24
4,819 4 0
  • 0
博主

一位佛系的前端开发者,略通摄影,乐于尝试新事物,热衷于美食。

友链
故事胶片
公众号
坚果大叔 执行上下文 卖坚果的怪叔叔 Dacking
Copyright © 2017-2026 卖坚果的怪叔叔

开往-友链接力

萌ICP备20230818号

苏ICP备18048410号-3
  • 首页
  • 生活
  • 技术
  • 记录
  • 工具箱
  • 关于我
# CSS # # JavaScript # # vue # # 微信 # # 生活 #
坚果大叔
345
文章
171
评论
432
喜欢