zoukankan      html  css  js  c++  java
  • Map和WeakMap

    Map和WeakMap

    Map

    JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
    为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键,是一种更完善的 Hash 结构实现。

    生成Map实例:

    const map1 = new Map();
    const map2 = new Map([
      ['name', '张三'],
      ['title', 'Author']
    ]);
    

    Map 实例的属性:

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

    Map实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。

    四个操作方法:

    • Map.prototype.set(key,value):设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。
    • Map.prototype.get(key):读取key对应的键值,如果找不到key,返回undefined
    • Map.prototype.has(key):返回一个布尔值,表示某个键是否在当前 Map 对象之中。
    • Map.prototype.delete(key):删除某个键,返回true。如果删除失败,返回false
    • Map.prototype.clear():清除所有成员,没有返回值。

    四个遍历方法:

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

    实例1:扩展对象

    当我们有一系列对象,想记录每个对象一种属性。假设有100只鸡,需要记录每只鸡的重量,有两种思路:

    1. 想办法用笔写到鸡身上
    2. 记录到一个本本上
    class Chicken {
    }
    // 100只鸡
    let chickenList = []
    for (let i = 0; i < 100; i++) {
      chickenList.push(new Chicken())
    }
                       
    // 方法1:记录到鸡身上
    chickenList.forEach(function(chicken, index){
    	chicken.weight = getWeight(chicken);
    });
    
    // 方法2:记录到本本上
    let notebook = [];
    chickenList.forEach(function(chicken, index){
    	notebook[index] = getWeight(chicken);
    });
    

    第1种思路存在以下问题:

    1. 破坏了鸡的卖相,有时候这是很严重的事情,比如你想把一只5斤的鸡当成6斤卖出去,结果鸡身上直接写“我只有5斤”(修改了原有对象,可能导致意外的行为)
    2. 可能碰到一些战斗鸡,一个字都写不上去(对象冻结了或者有不可覆盖的属性)
    3. 可能写到一些本来就写了字的地方,导致根本看不清(与对象原有属性冲突)

    再看第2种方法,存在以下问题:

    1. 本本无法和鸡精准地一一对应,只能靠一些索引或者标记(例如给每只鸡起一个名字)去(不可靠)地记录对应关系(无法精准地对比到是哪一个对象)
      这时候就可以使用Map扩展对象
    // 记录到另一个本本上
    let notebook = new Map();
    chickenList.forEach(function(chicken, index){
    	notebook.set(chicken, getWeight(chicken));
    });
    

    实例2:完善私有属性的实现

    回顾之前的Symbol实现的私有属性的版本里,仍然存在着可以被特殊api遍历的缺陷。

    基于Map的解决思路:

    用一个闭包内的Map来扩展每个生成的对象

    var Person = (function() {
      var map = new Map();
    
      function Person(name) {
        map.set(this,name);
      }
    
      Person.prototype.getName = function() {
        return map.get(this);
      };
    
      return Person;
    }());
    

    WeakMap

    与之前介绍的WeakSet 类似,WeakMapMap 有两个区别。

    • WeakMap的键只能是对象,而不能是其他类型的值。
    • WeakMap 中对键的引用是弱引用

    同样地,WeakMap 不能遍历,是因为成员都是弱引用,随时可能消失。

    WeakMap只有四个方法可用:get()set()has()delete()

    注意:WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。

    const wm = new WeakMap();
    let key = {};
    let obj = {foo: 1};
    
    wm.set(key, obj);
    obj = null;
    wm.get(key)
    

    实例:完善私有属性的实现

    前面基于Map的实现还存在一个问题:

    Person实例的外部引用消除时,闭包中的Map仍然有Person实例作为键的引用,Person实例不会被垃圾回收,必须等到所有的Person实例的外部引用消除,Map所在的闭包也会消除,最后Person实例才会被垃圾回收

    为了解决这个问题,使用WeakMap进一步完善:

    var Person = (function() {
      var wm = new WeakMap();
    
      function Person(name) {
        wm.set(this,name);
      }
    
      Person.prototype.getName = function() {
        return wm.get(this);
      };
    
      return Person;
    }());
    
  • 相关阅读:
    不务正业系列-浅谈《过气堡垒》,一个RTS玩家的视角
    [LeetCode] 54. Spiral Matrix
    [LeetCode] 40. Combination Sum II
    138. Copy List with Random Pointer
    310. Minimum Height Trees
    4. Median of Two Sorted Arrays
    153. Find Minimum in Rotated Sorted Array
    33. Search in Rotated Sorted Array
    35. Search Insert Position
    278. First Bad Version
  • 原文地址:https://www.cnblogs.com/xm0328/p/14127962.html
Copyright © 2011-2022 走看看