JavaScript < ES5、ES6、ES7、… >ECMAScript 6

ES6(九):Set和Map集合

2019-01-20  本文已影响113人  CodeMT

前面的话

  在ES6标准制定以前,由于可选的集合类型有限,数组使用的又是数值型索引,因而经常被用于创建队列和栈。如果需要使用非数值型索引,就会用非数组对象创建所需的数据结构,而这就是Set集合与Map集合的早期实现。本文将详细介绍ES6中的set和map集合

引入

  Set集合是一种无重复元素的列表,开发者们一般不会逐一读取数组中的元素,也不太可能逐一访问Set集合中的每个元素,通常的做法是检测给定的值在某个集合中是否存在
   Map集合内含多组键值对,集合中每个元素分别存放着可访问的键名和它对应的值,Map集合经常被用于缓存频繁取用的数据。在标准正式发布以前,开发者们已经在ES5中用非数组对象实现了类似的功能
  ES6新标准将Set集合与Map集合添加到JS中

【ES5】

在ES5中,开发者们用对象属性来模拟这两种集合

  let set = Object.create(null);
  set.foo =true;// 检查属性的存在性if (set.foo) {    // 一些操作}

Set集合

  ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。通过Set集合可以快速访问其中的数据,更有效地追踪各种离散值

  Set结构的实例有以下属性

  Set.prototype.constructor:构造函数,默认就是Set函数
  Set.prototype.size:返回Set实例的成员总数

  Set实例的操作方法(用于操作数据)包括以下4个

add(value):添加某个值,返回Set结构本身
has(value):返回一个布尔值,表示该值是否为Set的成员delete(value):删除某个`值,返回一个布尔值,表示删除是否成功
clear():清除所有成员,没有返回值

【创建Set集合、add()添加元素】

  调用new Set()创建Set集合,调用add()方法向集合中添加元素,访问集合的size属性可以获取集合中目前的元素数量

  let set =new Set();
  set.add(5);
  set.add("5");
  console.log(set.size); // 2

【has()检测元素】

通过has()方法可以检测Set集合中是否存在某个值

let set =new Set();
set.add(5);
set.add("5");
console.log(set.has(5));// true
console.log(set.has(6));// false

【delete()和clear()移除元素】

  调用delete()方法可以移除Set集合中的某一个元素,调用clear()方法会移除集合中的所有元素

  let set =new Set();
  set.add(5);
  set.add("5");
  console.log(set.has(5));// true
  set.delete(5);
  console.log(set.has(5));// false
  console.log(set.size);// 1
  set.clear();
  console.log(set.has("5"));// false
  console.log(set.size);// 0

【遍历操作】

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

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

forEach()

Set结构的实例的forEach方法,用于对每个成员执行某种操作,没有返回值

  let set =new Set(['a','b','c']);
  set.forEach((key, value, set) => {
      console.log(key,value,set);
   } )//a a ['a','b','c']//b b ['a','b','c']//c c ['a','b','c']

【将Set集合转换为数组】

将数组转换为Set集合的过程很简单,只需给Set构造函数传入数组即可;将Set集合再转回数组的过程同样很简单,需要用到展开运算符(...),它可以将数组中的元素分解为各自独立的函数参数。展开运算符也可以将诸如Set集合的可迭代对象转换为数组

  let set =new Set([1, 2, 3, 3, 3, 4, 5]),
  array =[...set];
  console.log(array); // [1,2,3,4,5]

WeakSet

将对象存储在Set的实例与存储在变量中完全一样,只要Set实例中的引用存在,垃圾回收机制就不能释放该对象的内存空间,于是之前提到的Set类型可以被看作是一个强引用的Set集合

  let set =new Set(),
  key ={};
  set.add(key);
  console.log(set.size); // 1// 取消原始引用key =null;
  console.log(set.size); // 1// 重新获得原始引用key = [...set][0];

【创建WeakSet集合】

【与Set集合的区别】

  除了以上主要区别之外,它们之间还有下面几个差别

1、在Weakset的实例中,如果向add()、has()和delete()这3个方法传入非对象参数都会导致程序报错

2、WeakSet集合不可迭代,所以不能被用于for-of循环

3、WeakSet集合不暴露任何迭代器(例如keys()和values()方法),所以无法通过程序本身来检测其中的内容

4、WeakSet集合不支持forEach()方法

5、WeakSet集合不支持size属性

Set类型可以用来处理列表中的值,但是不适用于处理键值对这样的信息结构。ES6也添加了Map集合来解决类似的问题

Map集合

  JS的对象(Object),本质上是键值对的集合(Hash结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制
  为了解决这个问题,ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现
  ES6中的Map类型是一种储存着许多键值对的有序列表,其中的键名和对应的值支持所有的数据类型。键名的等价性判断是通过调用Object.is()方法实现的,所以数字5与字符串"5"会被判定为两种类型,可以分别作为独立的两个键出现在程序中,这一点与对象不一样,因为对象的属性名总会被强制转换成字符串类型

【创建Map集合】

  如果要向Map集合中添加新的元素,可以调用set()方法并分别传入键名和对应值作为两个参数;如果要从集合中获取信息,可以调用get()方法

 let map =new Map();
 map.set("title", "Understanding ES6");
 map.set("year", 2017);
 console.log(map.get("title"));//"UnderstandingES6"
 console.log(map.get("year"));// 2017

【Map集合支持的方法】

在设计语言新标准时,委员会为Map集合与Set集合设计了如下3个通用的方法
  1、has(key)检测指定的键名在Map集合中是否已经存在
  2、delete(key)从Map集合中移除指定键名及其对应的值
  3、clear()移除Map集合中的所有键值对
Map集合同样支持size属性,其代表当前集合中包含的键值对数量

  let map =new Map();
  map.set("name", "huochai");
  map.set("age", 25);
  console.log(map.size); //2
  console.log(map.has("name"));//true
  console.log(map.get("name"));// "huochai"
  console.log(map.has("age"));// true
  console.log(map.get("age"));// 25
  map.delete("name");
  console.log(map.has("name"));// false
  console.log(map.get("name"));// undefined
  console.log(map.size);// 1
  map.clear();
  console.log(map.has("name"));// false
  console.log(map.get("name"));// undefined
  console.log(map.has("age"));// false
  console.log(map.get("age"));// undefined
  console.log(map.size);// 0

 【传入数组来初始化Map集合】

 【同名属性碰撞】

 【遍历】

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

map[Symbol.iterator] === map.entries// true

转为数组

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

forEach()

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

  const map =new Map([[1, 'one'],[2, 'two'],[3, 'three']]);
  map.forEach((value,key,map)=>{    //one 1 {1 => "one", 2 => "two", 3 => "three"}
      //two 2 {1 => "one", 2 => "two", 3 => "three"}
      //three 3 {1 => "one", 2 => "two", 3 => "three"}
  console.log(value,key,map);
  })

WeakMap

  WeakSet是引用Set集合,相对地,WeakMap是弱引用Map集合,也用于存储对象的弱引用
  WeakMap集合中的键名必须是一个对象,如果使用非对象键名会报错;集合中保存的是这些对象的弱引用,如果在弱引用之外不存在其他的强引用,引擎的垃圾回收机制会自动回收这个对象,同时也会移除WeakMap集合中的键值对。但是只有集合的键名遵从这个规则,键名对应的值如果是一个对象,则保存的是对象的强引用,不会触发垃圾回收机制
  WeakMap集合最大的用途是保存Web页面中的DOM元素,例如,一些为Web页面打造的JS库,会通过自定义的对象保存每一个引用的DOM元素
  使用这种方法最困难的是,一旦从Web页面中移除保存过的DOM元素,如何通过库本身将这些对象从集合中清除;否则,可能由于库过于庞大而导致内存泄露,最终程序不再正常执行。如果用WeakMap集合来跟踪DOM元素,这些库仍然可以通过自定义的对象整合每一个DOM元素,而且当DOM元素消失时,可以自动销毁集合中的相关对象

 【使用WeakMap集合】

 【WeakMap集合的初始化方法】

 【WeakMap集合支持的方法】

 【用途】

储存DOM元素

前面介绍过,WeakMap应用的典型场合就是DOM 节点作为键名

  let myElement = document.getElementById('logo');
  let myWeakmap =newWeakMap();
  myWeakmap.set(myElement, {timesClicked: 0});
  myElement.addEventListener('click',function() {
     let logoData =myWeakmap.get(myElement);
     logoData.timesClicked++;
  }, false);

部署私有属性

WeakMap的另一个用处是部署私有属性

  function Person(name) {  
     this._name =name;
  }
  Person.prototype.getName =function() {    
     return this._name;
  };

 【使用方式及使用限制】

上一篇 下一篇

猜你喜欢

热点阅读