ES6 |Set和Map数据结构

Set和Map数据结构

在这里插入图片描述

Set数据结构

ES6 提供了新的数据结构——Set。 Set 本身是一个构造函数,用来生成 Set 数据结构

1. 特性

  • Set成员的值都是唯一的,没有重复

    const set = new Set ([1, 2 , 3 , 4, 4]); 
    [ ...set] 
    //[1, 2 , 3 , 4]
    
  • 向Set加入值的时候,不会发生类型转换

  • Set内部判断两个值是否不同,使用的算法叫做“Same-value equality”,它类似于精确相等运算符(===

    在Set内部,两个NaN是相等

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

    两个对象总是不相等的

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

2. 属性与方法

  • Set.prototype.constructor:构造函数,默认就是Set函数

  • Set.prototype.size:返回Set实例的成员总数

  • add(value):添加某个值,返回Set结构本身

  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功

  • has(value):返回一个布尔值,表示该值是否为Set的成员

  • clear():清除所有成员,没有返回值

    s.add(1).add(2).add(2);	//[1,2]
    s.size // 2
    s.has(1) // true
    s.has(2) // true
    s.has(3) // false
    s.delete(2);
    s.has(2) // false
    
  • keys():返回键名的遍历器

  • values():返回键值的遍历器

  • entries():返回键值对的遍历器

    //由于 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"]
    

    Set结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法

    Set.prototype[Symbol.iterator] === Set.prototype.values	// true
    
    let set = new Set(['red', 'green', 'blue']);
    for (let x of set) {
      console.log(x);
    }
    // red
    // green
    // blue
    
  • forEach():对每个成员执行某种操作,没有返回值

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

3. 遍历的应用

//1. 扩展运算符(扩展运算符内部使用for...of循环,所以也可以用于Set结构)
let set = new Set(['red', 'green', 'blue']);
let arr = [...set];		// ['red', 'green', 'blue']
//2. 去除数组的重复成员
let arr = [3, 5, 2, 2, 5, 5];
let unique = [...new Set(arr)]	//方式1
let unique = new Set(arr)	//方式2
//3. map()和filter()
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}
//4. 并集(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}
//5. 在遍历中同步改变原来的Set结构
//方式1:利用原Set结构映射出一个新的结构,然后赋值给原来的Set结构
let set = new Set([1, 2, 3]);
set = new Set([...set].map(val => val * 2));	//[2,6,4]

//方式2:Array.from
let set = new Set([1, 2, 3]);
set = new Set(Array.from(set, val => val * 2));

WeakSet

WeakSet结构与Set类似,也是不重复的值的集合。但WeakSet的成员只能是对象,对象都是弱引用,不可遍历

1. 特性

  • WeakSet是一个构造函数

    var ws = new WeakSet();
    
  • 可以接受一个数组或类似数组的对象作为参数。(实际上,任何具有iterable接口的对象,都可以作为WeakSet的参数。)

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

2. 方法

  • WeakSet.prototype.add(value):向WeakSet实例添加一个新成员

  • WeakSet.prototype.delete(value):清除WeakSet实例的指定成员

  • WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在WeakSet实例之中

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

Map数据结构

1. 背景

对象(Object)本质上是键值对的集合(Hash结构),但是传统上只能用字符串当作键。下面代码原意是将一个DOM节点作为对象data的键,但是由于对象只接受字符串作为键名,所以element被自动转为字符串[object HTMLDivElement]

var data = {};
var element = document.getElementById('myDiv');

data[element] = 'metadata';
data['[object HTMLDivElement]'] // "metadata"

2. 特性

  • Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键

    var m = new Map();
    var o = {p: 'Hello World'};
    
    m.set(o, 'content')
    m.get(o) // "content"
    //解析:set方法将对象o当作m的一个键,然后又使用get方法读取这个键
    
    m.has(o) // true
    m.delete(o) // true
    m.has(o) // false
    
  • 可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组

    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"
    
  • 如果对同一个键多次赋值,后面的值将覆盖前面的值

    let map = new Map();
    map.set(1, 'aaa').set(1, 'bbb');
    map.get(1) // "bbb"
    
  • 只有对同一个对象的引用,Map结构才将其视为同一个键。Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键

    var map = new Map();
    map.set(['a'], 555);
    map.get(['a']) // undefined
    //解析:表面是针对同一个键,但实际上这是两个值,内存地址是不一样的
    

3. 属性和方法

  • size:返回Map结构的成员总数

    let map = new Map();
    map.set('foo', true);
    map.set('bar', false);
    map.size // 2
    
  • set(key, value):设置key所对应的键值,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键。set方法返回的是Map本身,因此可以采用链式写法

    var m = new Map();
    m.set("edition", 6)        // 键是字符串
    m.set(262, "standard")     // 键是数值
    m.set(undefined, "nah")    // 键是undefined
    
    let map = new Map()
      .set(1, 'a')
      .set(2, 'b')
      .set(3, 'c');
    
  • 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!
    
  • has(key):返回一个布尔值,表示某个键是否在Map数据结构中

  • delete(key):删除某个键,返回true。如果删除失败,返回false

  • clear():清除所有成员,没有返回值


4. 遍历的应用

  • keys():返回键名的遍历器

  • values():返回键值的遍历器

  • entries():返回所有成员的遍历器

  • forEach():遍历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本身没有mapfilter方法)

    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'}
    
  • forEach()

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

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

  • Map转为数组——使用扩展运算符(...

    let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
    [...myMap]	// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
    
  • 数组转为Map——Map构造函数

    new Map([[true, 7], [{foo: 3}, ['abc']]])
    // Map {true => 7, Object {foo: 3} => ['abc']}
    
  • 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 }
    
  • 对象转为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 ] ]
    
  • Map转为JSON

    //情况1: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}'
    
    //情况2: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"]]]'
    
  • JSON转为Map

    //情况1:JSON键名都是字符串
    function jsonToStrMap(jsonStr) {
      return objToStrMap(JSON.parse(jsonStr));
    }
    
    jsonToStrMap('{"yes":true,"no":false}')
    // Map {'yes' => true, 'no' => false}
    
    //情况2:JSON是一个数组
    function jsonToMap(jsonStr) {
      return new Map(JSON.parse(jsonStr));
    }
    
    jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
    // Map {true => 7, Object {foo: 3} => ['abc']}
    

WeakMap

1. 特性

WeakMap结构与Map结构基本类似,但它只接受对象作为键名(null除外)没有遍历操作、没有size属性,无法清空,即不支持clear方法

2. 方法

  • get()
  • set()
  • has()
  • delete()
posted @ 2020-09-01 13:45  sanhuamao  阅读(89)  评论(0编辑  收藏  举报