ES6学习笔记之Set和Map数据结构详解


Posted in Javascript onApril 07, 2017

本文实例讲述了ES6学习笔记之Set和Map数据结构。分享给大家供大家参考,具体如下:

一.Set

ES6提供了新的数据结构Set。类似于数组,只不过其成员值都是唯一的,没有重复的值。

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

1 . Set函数可以接受一个数组(或类似数组的对象)作为参数,用来初始化。

var s = new Set();
var set = new Set([1, 2, 3, 4, 4]);
[...set]  // [1, 2, 3, 4]
var items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5

2.Set 支持 add(item) 方法,用来向 Set 添加任意类型的元素,如果已经添加过则自动忽略;has(item) 方法用来检测 Set 中是否存在指定元素;delete(item) 方法用来从 Set 中删除指定元素;clear() 用来清空 Set;获取 Set 集合长度用 size 属性。如下:

JS

var set = new Set();
set.add(window);
set.has(window); // true
set.size; // 1
set.add(window);
set.add(1);
set.size; // 2
set.delete(window);
set.has(window); // false
set.clear();
set.size; // 0

Set 调用 add、has、delete 等方法时对 key 进行的比较,不做类型转换。向Set加入值的时候,不会发生类型转换,所以5和”5”是两个不同的值。

Set 中,NaN 只能添加一次;

Set 中,「-0」和「0 或 +0」可以同时存在,因为符号不一样;
另外,两个对象总是不相等的。

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

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

3.除数组重复成员的方法。

var set = new Set([1, 2, 3, 4, 4]);
[...set]  // [1, 2, 3, 4]

4.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

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

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

5.Set的遍历

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

keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器

key方法、value方法、entries方法返回的都是遍历器对象

由于Set结构没有键名,只有键值(或者说键名和键值是同一个值),所以key方法和value方法的行为完全一致。

entries方法返回的遍历器,同时包括键名和键值,所以每次输出一个数组,它的两个成员完全相等。

forEach():使用回调函数遍历每个成员

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"]
//可以省略values方法,直接用for...of循环遍历Set。
for (let x of set) {
 console.log(x);
}
// red
// green
// blue

Set结构的实例的forEach方法,用于对每个成员执行某种操作,没有返回值。该函数的参数依次为键值、键名、集合本身(下例省略了该参数)。另外,forEach方法还可以有第二个参数,表示绑定的this对象。

let set = new Set([1, 2, 3]);
set.forEach((value, key) => console.log(value * 2) )
// 2
// 4
// 6

6.应用

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

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

WeakSet结构与Set类似,也是不重复的值的集合。但是,它与Set有两个区别。

首先,WeakSet的成员只能是对象,而不能是其他类型的值。

其次,WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用

三、Map

1、Map结构的目的和基本用法:

JavaScript的对象(Object),本质上是键值对的集合(Hash结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。

为了解决这个问题,ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比Object更合适。

对象作为参数:

var m = new Map();
var o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content" 使用set方法,将对象o当作m的一个键,然后又使用get方法读取这个键,

作为构造函数,Map也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。

var map = new Map([
 ['name', '张三'],
 ['title', 'Author']
]);
map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"

注意:

a. 字符串true和布尔值true是两个不同的键。

b. 如果对同一个键多次赋值,后面的值将覆盖前面的值。

let map = new Map();
map.set(1, 'aaa')
map.set(1, 'bbb');
map.get(1) // "bbb"

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

d.只有对同一个对象的引用,Map结构才将其视为同一个键。这一点要非常小心。同理,同样的值的两个实例,在Map结构中被视为两个键。
好处:

由上可知,Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。

e. 如果Map的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map将其视为一个键,包括0和-0。另外,虽然NaN不严格相等于自身,但Map将其视为同一个键。

let map = new Map();
map.set(NaN, 123);
map.get(NaN) // 123
map.set(-0, 123);
map.get(+0) // 123

2.Map实例属性和操作方法

(1)size属性:返回Map结构的成员总数。

let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2

(2)set(key, value) 方法设置key所对应的键值,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键。

var m = new Map();
m.set("edition", 6)    // 键是字符串
m.set(262, "standard")   // 键是数值
m.set(undefined, "nah")  // 键是undefined

set方法返回的是Map本身,因此可以采用链式写法。

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

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

var m = new Map();
var hello = function() {console.log("hello");}
m.set(hello, "Hello ES6!") // 键是函数
m.get(hello) // Hello ES6!

(4)has(key)方法返回一个布尔值,表示某个键是否在Map数据结构中。

var m = new Map();
m.set("edition", 6);
m.set(262, "standard");
m.set(undefined, "nah");
m.has("edition")   // true
m.has("years")    // false
m.has(262)      // true
m.has(undefined)   // true

(5)delete(key)方法删除某个键,返回true。如果删除失败,返回false。

var m = new Map();
m.set(undefined, "nah");
m.has(undefined)   // true
m.delete(undefined)
m.has(undefined)    // false

(6)clear() 方法清除所有成员,没有返回值。

let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
map.clear()
map.size // 0

3.遍历方法

Map原生提供三个遍历器生成函数和一个遍历方法。

keys():返回键名的遍历器。
values():返回键值的遍历器。
entries():返回所有成员的遍历器。
forEach():遍历Map的所有成员。

需要特别注意的是,Map的遍历顺序就是插入顺序。

let map = new Map([
 ['F', 'no'],
 ['T', 'yes'],
]);
for (let key of map.keys()) {
 console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
 console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
 console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
 console.log(key, value);
}
// 等同于使用map.entries()
for (let [key, value] of map) {
 console.log(key, value);
}

Map结构转为数组结构,比较快速的方法是结合使用扩展运算符(…)。

let 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']]

结合数组的map方法、filter方法,可以实现Map的遍历和过滤(Map本身没有map和filter方法)。

let map0 = new Map()
 .set(1, 'a')
 .set(2, 'b')
 .set(3, 'c');
let map1 = new Map(
 [...map0].filter(([k, v]) => k < 3)
);
// 产生Map结构 {1 => 'a', 2 => 'b'}
let map2 = new Map(
 [...map0].map(([k, v]) => [k * 2, '_' + v])
  );
// 产生Map结构 {2 => '_a', 4 => '_b', 6 => '_c'}

Map还有一个forEach方法,与数组的forEach方法类似,也可以实现遍历。

map.forEach(function(value, key, map) {
 console.log("Key: %s, Value: %s", key, value);
});

4、与其他数据结构的转换

(1)Map转为数组:扩展运算符(…)

let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]

(2)数组转为Map:将数组转入Map构造函数

new Map([[true, 7], [{foo: 3}, ['abc']]])
// Map {true => 7, Object {foo: 3} => ['abc']}

(3)Map转为对象:前提是 所有Map的键都是字符串,它可以转为对象。

function strMapToObj(strMap) {
 let obj = Object.create(null);
 for (let [k,v] of strMap) {
  obj[k] = v;
 }
 return obj;
}
let myMap = new Map().set('yes', true).set('no', false);
strMapToObj(myMap)
// { yes: true, no: false }

(4)对象转为Map

function objToStrMap(obj) {
 let strMap = new Map();
 for (let k of Object.keys(obj)) {
  strMap.set(k, obj[k]);
 }
 return strMap;
}
objToStrMap({yes: true, no: false})
// [ [ 'yes', true ], [ 'no', false ] ]

(5)Map转为JSON

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

function strMapToJson(strMap) {
 return JSON.stringify(strMapToObj(strMap));
}
let myMap = new Map().set('yes', true).set('no', false);
strMapToJson(myMap)
// '{"yes":true,"no":false}'

另一种情况是,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"]]]'

(6)JSON转为Map

JSON转为Map,正常情况下,所有键名都是字符串。

function jsonToStrMap(jsonStr) {
 return objToStrMap(JSON.parse(jsonStr));
}
jsonToStrMap('{"yes":true,"no":false}')
// Map {'yes' => true, 'no' => false}

但是,有一种特殊情况,整个JSON就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为Map。这往往是数组转为JSON的逆操作。

function jsonToMap(jsonStr) {
 return new Map(JSON.parse(jsonStr));
}
jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
// Map {true => 7, Object {foo: 3} => ['abc']}

四、WeakMap

结构与Map结构基本类似,唯一的区别是它只接受对象作为键名(null除外),不接受其他类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制。

希望本文所述对大家ECMAScript程序设计有所帮助。

Javascript 相关文章推荐
15个款优秀的 jQuery 图片特效插件推荐
Nov 21 Javascript
Jquery增加鼠标中间功能mousewheel的实例代码
Sep 05 Javascript
Javascript访问器属性实例分析
Dec 30 Javascript
jQuery设置和移除文本框默认值的方法
Mar 09 Javascript
jQuery使用toggleClass方法动态添加删除Class样式的方法
Mar 26 Javascript
基于jquery实现日历签到功能
Sep 11 Javascript
jQuery 选择同时包含两个class的元素的实现方法
Jun 01 Javascript
Vue.js上下滚动加载组件的实例代码
Jul 17 Javascript
jquery ajax异步提交表单数据的方法
Oct 27 jQuery
js设置鼠标悬停改变背景色实现详解
Jun 26 Javascript
微信小程序实现多行文字超出部分省略号显示功能
Oct 23 Javascript
JavaScrip如果基于url实现图片下载
Jul 03 Javascript
Vue表单验证插件Vue Validator使用方法详解
Apr 07 #Javascript
js 数字、字符串、布尔值的转换方法(必看)
Apr 07 #Javascript
利用js的闭包原理做对象封装及调用方法
Apr 07 #Javascript
JS实现的模仿QQ头像资料卡显示与隐藏效果
Apr 07 #Javascript
socket.io实现在线群聊功能
Apr 07 #Javascript
JS+HTML5 FileReader对象用法示例
Apr 07 #Javascript
微信小程序实现图片轮播及文件上传
Apr 07 #Javascript
You might like
第十三节--对象串行化
2006/11/16 PHP
php时间戳格式化显示友好的时间函数分享
2014/10/21 PHP
PHP yii实现model添加默认值的方法(两种方法)
2016/11/10 PHP
枚举JavaScript对象的函数
2006/12/22 Javascript
几个比较经典常用的jQuery小技巧
2010/03/01 Javascript
jQuery中$.click()无效问题分析
2015/01/29 Javascript
javascript实现字符串反转的方法
2015/02/05 Javascript
js如何改变文章的字体大小
2016/01/08 Javascript
理解Javascript文件动态加载
2016/01/29 Javascript
用JavaScript动态建立或增加CSS样式表的实现方法
2016/05/20 Javascript
js 上传文件预览的简单实例
2016/08/16 Javascript
js严格模式总结(分享)
2016/08/22 Javascript
深入分析node.js的异步API和其局限性
2016/09/05 Javascript
JavaScript中this的用法实例分析
2016/12/19 Javascript
vue2.0 中#$emit,$on的使用详解
2017/06/07 Javascript
webpack 打包压缩js和css的方法示例
2018/03/20 Javascript
laydate如何根据开始时间或者结束时间限制范围
2018/11/15 Javascript
JS字典Dictionary类定义与用法示例
2019/02/01 Javascript
Python使用numpy模块创建数组操作示例
2018/06/20 Python
解决tensorflow1.x版本加载saver.restore目录报错的问题
2018/07/26 Python
Python面向对象程序设计之类的定义与继承简单示例
2019/03/18 Python
python中报错&quot;json.decoder.JSONDecodeError: Expecting value:&quot;的解决
2019/04/29 Python
python 实现交换两个列表元素的位置示例
2019/06/26 Python
python更新数据库中某个字段的数据(方法详解)
2020/11/18 Python
浅析CSS3 用text-overflow解决文字排版问题
2020/10/28 HTML / CSS
详解HTML5 录音的踩坑之旅
2017/12/26 HTML / CSS
size?德国官方网站:英国伦敦的球鞋精品店
2018/03/17 全球购物
Schecker荷兰:狗狗用品和配件
2019/06/06 全球购物
实习单位鉴定评语
2014/04/26 职场文书
户籍证明书标准模板
2014/09/10 职场文书
品质保证书格式
2015/02/28 职场文书
向雷锋同志学习倡议书
2015/04/27 职场文书
学校标语口号大全
2015/12/26 职场文书
制定企业培训计划的五大要点!
2019/07/10 职场文书
Python集合set()使用的方法详解
2022/03/18 Python
Python matplotlib绘制雷达图
2022/04/13 Python