JvavScript:ES6中的Set、WeakSet、Map、WeakMap详解

1.Set

基本用法

ES6提供了新的数据结构Set。它是一个构造函数,通过new生成的Set数据结构类似于数组,允许存储任何类型的值,无论是原始值或者对象引用,但是成员的值是唯一的,没有重复的值。

let s = new Set([1,2,3,1,3,4])   //=> {1, 2, 3, 4}

语法:new Set([val]);

参数:传递一个具有iterable接口的数据结构用来初始化,把它的所有元素不重复的添加到新的Set中。如果不指定参数或者参数为null,则新的Set为空。

返回值:一个新的Set对象。

具有iterable数据结构的数据类型有:Array、String、Map、Set、TypedArray(类数组)、函数的arguments对象、NodeList对象

let set1 = new Set('dafdv');    //=> {"d", "a", "f", "v"}
let set2 = new Set([1,2,3,4,1,2,3]);  //=>{1, 2, 3, 4}

可以利用Set成员没有重复的特点进行数组去重,同样也可以对字符串进行去重

let ary = [1,2,3,1,3,4];
ary = [...new Set(ary)];
console.log(ary); //=>[1,2,3,4]

let str = 'ddewcads';
str = [...new Set(str)].join('');
console.log(str); //=>'dewcas'

Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”,它类似于精确相等运算符(===),但是在Set中NaN等于NaN,而精确相等运算符认为NaN不等于自身。

LET set = new Set([NaN,NaN,,undefined]) //=>{NaN, undefined} 
//ES6对数组的空位有了统一的处理,明确规定将空位转换为undefined

Set实例的属性和方法

Set 结构的实例有以下属性

  • Set.prototype.constructor:默认是Set构造函数
  • Set.prototype.size:返回Set实例的成员总数
let set = new Set('dexs');
console.log(set.size);  //=>4

Set实例的方法可以分为两大类:操作方法和遍历方法

操作方法

  • Set.prototype.add(val):增加某个值,返回Set结构本身
  • Set.prototype.delete(val):删除某个值,返回一个布尔值,表示删除是否成功
  • Set.prototype.has(val):返回一个布尔值,白女士该值是否为Set的成员
  • Set.prototype.clear():清除所有成员,没有返回值
    举例如下
let s = new Set();
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

遍历操作

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

其中keys(),values(),entries()三个方法一般结合for…of 循环使用。因为这三个方法返回的是Iterator对象。由于Set结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为一样。

let set = new Set(['aaa', 'bbb', 'ccc']);

for (let item of set.keys()) {
  console.log(item);
}
// aaa
// bbb
// ccc

for (let item of set.values()) {
  console.log(item);
}
// aaa
// bbb
// ccc

for (let item of set.entries()) {
  console.log(item);
}
// ["aaa", "aaa"]
// ["bbb", "bbb"]
// ["ccc", "ccc"]

Set 结构的实例和数组一样,也有forEach方法,可以遍历每个成员,没有返回值

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

2.WeakSet

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

  1. WeakSet的成员只能是对象,而不能是其它类型的值
let ws = new WeakSet();
ws.add(123)
// TypeError: Invalid value used in weak set
ws.add(Symbol('sdfda'))
// TypeError: invalid value used in weak set

试图向 WeakSet 添加基本值,结构都是报错,因为WeakSet 只能放置对象。

  1. WeakSet 中的对象都是弱引用,即浏览器的垃圾回收机制不考虑 WeakSet 对该对象的引用。也可以理解为该对象只要其它东西不在引用,只有 WeakSet 引用,垃圾回收机制会销毁该对象。

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

let ws = new WeakSet();

WeakSet 可以接受具有Iterable 接口的对象作为参数,并且该对象必须是二维及二维以上的

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

以上示例组数成员必须是引用数据类型

WeakSet 实例的方法

  • WeakSet.prototype.add(value):向WeakSet 实例添加一个新的成员
  • WeakSet.prototype.delete(value):清除 WeakSet 实例的只当成员
  • WeakSet.prototype.has(value):返回一个布尔值,表示 WeakSet 中是否存在某个值
    举例如下:
let ws = new WeakSet();

let obj = {};

let foo = {};

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

ws.has(window); // true
ws.has(foo);    // false

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

WeakSet 和Set不同,没有 Size 属性,没有办法遍历它的成员

let ws = new WeakSet();
ws.size  // undefined
ws.forEach(item=>{
	console.log(item);
}) // Uncaught TypeError: ws.forEach is not a function

WeakSet 不能遍历,是因为它的每个成员都是弱引用,垃圾回收机制不考虑它的引用,随时都有可能消失,所以在遍历的时候,可能刚刚遍历完,就不存在了。

3.Map

基本用法

在JS中,普通对象,本质上是键值对的集合(Hash结构),但是传统上只能把字符串当作键。
为了解决这个问题,ES6 提供了Map 数据结构,它类似于对象,也是键值对的集合,但是各种类型的值包括对象都可当作键,是一种比较完善的 Hash 结构。

let map = new Map([
  ['name', '张三'],
  ['title', 'Author']
]);
console.log(map); // {"name" => "张三", "title" => "Author"}
map.set('name','李四');
map.set('name','王五');
map.get('name') // 王五
map.get('age'); // undefined
console.log(map); // {"name" => "王五", "title" => "Author"}

上面代码中,使用 Map 结构中的 set 和 get 方法设置和读取 name 键,并且一个键多次设置值后面会覆盖前面的,如果读取一个未知的键,返回undefined。

// 例一
let map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined

// 例二
let map = new Map();
let k1 = ['a'];
let k2 = ['a'];

map.set(k1, 111)
map.set(k2, 222);

map.get(k1) // 111
map.get(k2) // 222

该代码例一, set 和 get 方法,表面是针对同一个键,但是实际上这是两个数组不同的实例,内存地址不一样,因此两者操作的并非同意换个键,所以 get 返回的undefined。同样例二操作的两两个键看起来虽然一样,但是最后却可以获取到两个值,因为 k1 === k2 等于false。由此可以看出,Map的键如果是对象,则该键和内存地址绑定,只要内存地址不同,就是两个不同的键。

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

map.set(true, 1);
map.set('true', 2);
map.get(true) // 1

map.set(undefined, 3);
map.set(null, 4);
map.get(undefined) // 3

map.ste(NaN,123);
map.get(NaN); // 123

该例子可以看出,在Map中 -0 等于 +0,是一个键。布尔值 true 和字符串 ‘true’则是两个不同的键;undefined 和 null 也是两个不同的键; NaN 虽然不等于自身,但是 Map 将其是为一个键。

Map实例的属性和操作方法

属性

  • size属性:返回 Map 结构的成员总数
  • constructor属性:等于 Map 构造函数

操作方法

  • Map.prototype.set(key,value)
    设置 key 对应得值 value,然后返回整个 Map 结构。如果设置得时候 key 已经存在,则是修改值,否则就是设置新的值。
let map = new Map().set(1, 'a').set(2, 'b').set(3, 'c'); 
// {1 => "a", 2 => "b", 3 => "c"}

因为每次设置返回的都是 Map 结构,所以可以使用链式写法

  • Map.prototype.get(key)
    获取的时候读取 key 对应的值,如果没有返回undefined。
let m = new Map();
m.set('hello', 'Hello ES6!') 
m.get('hello')  // Hello ES6!
m.get('sss') // undefined
  • Map.prototype.has(key)
    has 方法返回一个布尔值,表示某个键是否在当前 Map 对象之中
let 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
  • Map.prototype.delete(key)
    delete方法删除某个键,返回true。如果删除失败,返回false。

  • Map.prototype.clear()
    clear方法清除所有成员,没有返回值。

遍历方法

  • Map.prototype.keys():返回键名的遍历器。
  • Map.prototype.values():返回键值的遍历器。
  • Map.prototype.entries():返回所有成员的遍历器。
  • Map.prototype.forEach():遍历 Map 的所有成员。
    需要注意的是,Map 的遍历顺序就是插入顺序。
    用法和 Set 一样
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);
}
// "F" "no"
// "T" "yes"

// 等同于使用map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

Map 结构转换为数组结构,一般都是以于扩展运算符(…)。

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

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

  1. 转换为数组
    前面已经说过了,一般基于扩展运算符转换

  2. 数组转换为 Map
    将数组传入 Map 构造函数,就可以转换为 Map。

let map = new Map([
  [true, 7],
  [{foo: 3}, ['abc']]
]);
//  {true => 7,Object {foo: 3} => ['abc']}
  1. Map 转换为对象
    如果所有 Map 的键都是字符串,它也可以无损的转换为对象
function strMapToObj(strMap) {
  let obj = Object.create(null);
  for (let [k,v] of strMap) {
    obj[k] = v;
  }
  return obj;
}

const myMap = new Map()
  .set('yes', true)
  .set('no', false);
strMapToObj(myMap)
// { yes: true, no: false }

如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。

  1. 对象转换为 Map
    对象转换为 Map 可以借用 Object.entries()。
let obj = {"a":1, "b":2};
let map = new Map(Object.entries(obj));

4.WeakMap

WeakMap 与 Map 的区别有两点

  1. WeakMap 只接受对象作为键名,不接受其它类型的值作为键名
  2. WeakMap的键名对对象的引用不计入垃圾回收机制。
let map = new WeakMap();
map.set(1, 2)
// TypeError: 1 is not an object!
map.set(Symbol(), 2)
// TypeError: Invalid value used as weak map key
map.set(null, 2)
// TypeError: Invalid value used as weak map key

WeakMap 的属性和方法
WeakMap 和 WeakSet 一样没有遍历操作,即没有 keys()、values()、entries() 方法,也没有size属性。
WeakMap 无法清空,即不支持 clear方法。
WeakMap 只有四个方法可以使用:get()、set()、has()、delete()。

const wm = new WeakMap();

// size、forEach、clear 方法都不存在
wm.size // undefined
wm.forEach // undefined
wm.clear // undefined

参考

阮一峰ES6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值