目录

set

1基本用法

2Set 实例的属性和方法

3遍历操作

3.1 keys() , values() , entries()

3.2 forEach()

3.3遍历的应用

weakset

含义

语法

总结


set

1基本用法

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set 本身是一个构造函数,用来生成 Set 数据结构。

const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4

上面代码通过 add 方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值。
Set 函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。

// 例一
const set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]
// 例二
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5
// 例三
function divs () {
return [...document.querySelectorAll('div')];
}
const set = new Set(divs());
set.size // 56
// 类似于
divs().forEach(div => set.add(div));
set.size // 56

上面代码中,例一和例二都是 Set 函数接受数组作为参数,例三是接受类似数组的对象作为参数。
上面代码中,也展示了一种去除数组重复成员的方法。

// 去除数组的重复成员
[...new Set(array)]

向 Set 加入值的时候,不会发生类型转换,所以 5 和 "5" 是两个不同的值。Set 内部判断两个值是否不同,使用的算法叫做“Same-value equality”,它
类似于精确相等运算符( === ),主要的区别是 NaN 等于自身,而精确相等运算符认为 NaN 不等于自身。

let set = new Set();
let a = NaN;
let b = NaN;
set.add(a);
set.add(b);
set // Set {NaN}

上面代码向 Set 实例添加了两个 NaN ,但是只能加入一个。这表明,在 Set 内部,两个 NaN 是相等。
另外,两个对象总是不相等的

let set = new Set();
set.add({});
set.size // 1
set.add({});
set.size // 2

上面代码表示,由于两个空对象不相等,所以它们被视为两个值

2Set 实例的属性和方法

Set 结构的实例有以下属性。
Set.prototype.constructor :构造函数,默认就是 Set 函数。
Set.prototype.size :返回 Set 实例的成员总数。
Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。
add(value) :添加某个值,返回 Set 结构本身。

delete(value) :删除某个值,返回一个布尔值,表示删除是否成功。
has(value) :返回一个布尔值,表示该值是否为 Set 的成员。
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 properties = {
'width': 1,
'height': 1
};
if (properties[someName]) {
// do something
}
// Set的写法
const properties = new Set();
properties.add('width');
properties.add('height');
if (properties.has(someName)) {
// do something
}

Array.from 方法可以将 Set 结构转为数组。

const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);

这就提供了去除数组重复成员的另一种方法。

function dedupe(array) {
return Array.from(new Set(array));
}
dedupe([1, 1, 2, 3]) // [1, 2, 3]

3遍历操作

Set 结构的实例有四个遍历方法,可以用于遍历成员。
keys() :返回键名的遍历器
values() :返回键值的遍历器
entries() :返回键值对的遍历器
forEach() :使用回调函数遍历每个成员
需要特别指出的是, Set 的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set 保存一个回调函数列表,调用时就能保证按照添加顺序调用。

3.1 keys() , values() , entries()

keys 方法、 values 方法、 entries 方法返回的都是遍历器对象(详见《Iterator 对象》一章)。由于 Set 结构没有键名,只有键值(或者说键名和键值
是同一个值),所以 keys 方法和 values 方法的行为完全一致。

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"]

上面代码中, entries 方法返回的遍历器,同时包括键名和键值,所以每次输出一个数组,它的两个成员完全相等。
Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的 values 方法。

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

这意味着,可以省略 values 方法,直接用 for...of 循环遍历 Set。

let set = new Set(['red', 'green', 'blue']);
for (let x of set) {
console.log(x);
}
// red
// green
// blue

3.2 forEach()

Set 结构的实例与数组一样,也拥有 forEach 方法,用于对每个成员执行某种操作,没有返回值。

set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9

上面代码说明, forEach 方法的参数就是一个处理函数。该函数的参数与数组的 forEach 一致,依次为键值、键名、集合本身(上例省略了该参数)。这
里需要注意,Set 结构的键名就是键值(两者是同一个值),因此第一个参数与第二个参数的值永远都是一样的。
另外, forEach 方法还可以有第二个参数,表示绑定处理函数内部的 this 对象。

3.3遍历的应用

扩展运算符( ... )内部使用 for...of 循环,所以也可以用于 Set 结构。

let set = new Set(['red', 'green', 'blue']);
let arr = [...set];
// ['red', 'green', 'blue']

扩展运算符和 Set 结构相结合,就可以去除数组的重复成员

let arr = [3, 5, 2, 2, 5, 5];
let unique = [...new Set(arr)];
// [3, 5, 2]

而且,数组的 map 和 filter 方法也可以用于 Set 了。

let set = new Set([1, 2, 3]);
set = new Set([...set].map(x => x * 2));
// 返回Set结构:{2, 4, 6}
let set = new Set([1, 2, 3, 4, 5]);
set = new Set([...set].filter(x => (x % 2) == 0));
// 返回Set结构:{2, 4}

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

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

上面代码提供了两种方法,直接在遍历操作中改变原来的 Set 结构。

weakset

含义

WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。
首先,WeakSet 的成员只能是对象,而不能是其他类型的值

const ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set

上面代码试图向 WeakSet 添加一个数值和 Symbol 值,结果报错,因为 WeakSet 只能放置对象。
其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机
制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。
这是因为垃圾回收机制依赖引用计数,如果一个值的引用次数不为 0 ,垃圾回收机制就不会释放这块内存。结束使用该值之后,有时会忘记取消引用,导
致内存无法释放,进而可能会引发内存泄漏。WeakSet 里面的引用,都不计入垃圾回收机制,所以就不存在这个问题。因此,WeakSet 适合临时存放一
组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。
由于上面这个特点,WeakSet 的成员是不适合引用的,因为它会随时消失。另外,由于 WeakSet 内部有多少个成员,取决于垃圾回收机制有没有运行,
运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakSet 不可遍历。
这些特点同样适用于本章后面要介绍的 WeakMap 结构

语法

WeakSet 是一个构造函数,可以使用 new 命令,创建 WeakSet 数据结构

const ws = new WeakSet();

作为构造函数,WeakSet 可以接受一个数组或类似数组的对象作为参数。(实际上,任何具有 Iterable 接口的对象,都可以作为 WeakSet 的参数。)
该数组的所有成员,都会自动成为 WeakSet 实例对象的成员。

const a = [[1, 2], [3, 4]];
const ws = new WeakSet(a);
// WeakSet {[1, 2], [3, 4]}

上面代码中, a 是一个数组,它有两个成员,也都是数组。将 a 作为 WeakSet 构造函数的参数, a 的成员会自动成为 WeakSet 的成员。
注意,是 a 数组的成员成为 WeakSet 的成员,而不是 a 数组本身。这意味着,数组的成员只能是对象

const b = [3, 4];
const ws = new WeakSet(b);
// Uncaught TypeError: Invalid value used in weak set(…)

上面代码中,数组 b 的成员不是对象,加入 WeaKSet 就会报错。
WeakSet 结构有以下三个方法。
WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中。

下面是一个例子。

const ws = new WeakSet();
const obj = {};
const foo = {};
ws.add(window);
ws.add(obj);
ws.has(window); // true
ws.has(foo); // false
ws.delete(window);
ws.has(window); // false

WeakSet 没有 size 属性,没有办法遍历它的成员。

ws.size // undefined
ws.forEach // undefined
ws.forEach(function(item){ console.log('WeakSet has ' + item)})
// TypeError: undefined is not a function

上面代码试图获取 size 和 forEach 属性,结果都不能成功。
WeakSet 不能遍历,是因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。WeakSet 的一个
用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏。
下面是 WeakSet 的另一个例子。

const foos = new WeakSet()
class Foo {
constructor() {
foos.add(this)
}
method () {
if (!foos.has(this)) {
throw new TypeError('Foo.prototype.method 只能在Foo的实例上调用!');
}
}
}

上面代码保证了 Foo 的实例方法,只能在 Foo 的实例上调用。这里使用 WeakSet 的好处是, foos 对实例的引用,不会被计入内存回收机制,所以删除实
例的时候,不用考虑 foos ,也不会出现内存泄漏。

总结

本博客源于本人阅读相关书籍和视频总结,创作不易,谢谢点赞支持。学到就是赚到。我是歌谣,励志成为一名优秀的技术革新人员。

欢迎私信交流,一起学习,一起成长。

推荐链接 其他文件目录参照

“睡服“面试官系列之各系列目录汇总(建议学习收藏)

“睡服”面试官系列第六篇之set数据结构(建议收藏学习)相关推荐

  1. “睡服”面试官系列第十七篇之Reflect(建议收藏学习)

    目录 1. 概述 2. 静态方法 2.1Reflect.get(target, name, receiver) 2.2Reflect.set(target, name, value, receiver ...

  2. “睡服”面试官系列第八篇之iterator(建议收藏学习)

    目录 1. Iterator(遍历器)的概念 2. 默认 Iterator 接口 3. 调用 Iterator 接口的场合 3.1解构赋值 3.2扩展运算符 3.3yield* 3.4其他场合 4. ...

  3. “睡服”面试官系列第五篇之proxy(建议收藏学习)

    目录 1. 概述 2. Proxy 实例的方法 2.1get() 2.2set() 2.3apply() 2.4has() 2.5construct() 2.7deleteProperty() 2.8 ...

  4. “睡服”面试官系列第七篇之map数据结构(建议收藏学习)

    目录 1map 1.1含义和基本用法 1.2实例的属性和操作方法 1.2.1size属性 1.2.2set(key, value) 1.2.3get(key) 1.2.4has(key) 1.2.5d ...

  5. “睡服”面试官系列第二十三篇之修饰器(建议收藏学习)

    目录 1. 类的修饰 2. 方法的修饰 3. 为什么修饰器不能用于函数? 4. core-decorators.js 4.1@autobind 4.2@readonly 4.3@override 4. ...

  6. “睡服”面试官系列第二十一篇之class基本语法(建议收藏学习)

    目录 1. 简介 2. 严格模式 3. constructor 方法 4. 类的实例对象 5. Class 表达式 6. 不存在变量提升 7. 私有方法 8. 私有属性 9. this 的指向 10. ...

  7. “睡服”面试官系列第二十篇之generator函数的异步应用(建议收藏学习)

    目录 1. 传统方法 2. 基本概念 2.1异步 2.2回调函数 2.3Promise 3. Generator 函数 3.1协程 3.2协程的 Generator 函数实现 3.3Generator ...

  8. “睡服”面试官系列第十三篇之函数的扩展(建议收藏学习)

    目录 1. 函数参数的默认值 1.1基本用法 1.2与解构赋值默认值结合使用 1.3参数默认值的位置 1.4函数的 length 属性 1.5作用域 1.6应用 2. rest 参数 3. 严格模式 ...

  9. “睡服”面试官系列第十一篇之module加载实现(建议收藏学习)

    目录 1. 浏览器加载 1.1传统方法 1.2加载规则 2. ES6 模块与 CommonJS 模块的差异 3. Node 加载 3.1概述 3.2内部变量 4ES6 模块加载 CommonJS 模块 ...

最新文章

  1. dqn在训练过程中loss越来越大_用DQN算法玩FlappyBird
  2. Markdown 语法说明 (简体中文版)
  3. MyBatis 关系映射XML配置
  4. 直连串口线、交叉串口线
  5. ZSKAME大白菜2013官网下载
  6. Java 网络实例二(查看主机指定文件的最后修改时间、Socket实现多线程服务器程序、Socket连接到指定主机、网页抓取)
  7. CCF201412-1 门禁系统
  8. ubuntu使用python opencv_Ubuntu中“利用Opencv + python进行特征匹配”的环境搭建
  9. BZOJ3252攻略——长链剖分+贪心
  10. 关于何种情况下使用DataGrid、DataList或Repeater的一些讨论(1) ambushaa [翻译] [转]
  11. 大数据之项目需求及架构设计
  12. TP5.1导出指定的多个日期的数据记录
  13. 番外4. Python OpenCV 中鼠标事件相关处理与常见问题解决方案
  14. Python的7大就业方向,转行的人适合哪个方向?学了Python能干什么?
  15. 恒流LED升压驱动芯片2.5V~24V输入【待机功耗低 电流精度高3%】惠海半导体H6911方案分析
  16. 制造业数字化转型的意义是什么?
  17. 【html】【微信小程序】将图片压缩,文件上传的方法
  18. 无刷新假象   实现简易文件上传
  19. vue3中 v-md-editor 编辑器的基本使用分享
  20. 2012年科技行业那些事:IT巨头加紧冲刺步伐

热门文章

  1. 18、Java并发性和多线程-饥饿与公平
  2. 初识JavaScript———JavaScript注意事项(1)
  3. 表单提交中get 和post方式的区别
  4. ADO.NET的记忆碎片(七)
  5. 值大于为此列指定的允许精度_电能质量测试精度会受到哪些因素影响?如何解决?...
  6. pytorch线性回归代码_[PyTorch 学习笔记] 1.3 张量操作与线性回归
  7. go 类型断言_(57)接口的类型断言
  8. 高新园区到大连计算机学校,大连高新区中心小学
  9. VC6中使用内存DC加载并显示JPG图片的注意事项
  10. gcc编译选项-Os的用法