# Map 对象

  • ES6提供了 Map 数据结构,本质上也是键值对的集合(Hash结构),但"键"不限于字符串,各种类型的值都可以当键。
  • 键值的去重是基于Object.is进行比较的。

# Object.is(key1, key2)

===运算符的差别就是,Object.is有以下特殊的

  • 数字 -0 与 +0 视为不相等
  • NaN 与 NaN 视为相等

# Object.is例子

Object.is('foo', 'foo');     // true
Object.is(window, window);   // true

Object.is('foo', 'bar');     // false
Object.is([], []);           // false

var foo = { a: 1 };
var bar = { a: 1 };
Object.is(foo, foo);         // true
Object.is(foo, bar);         // false

Object.is(null, null);       // true

// 特例
Object.is(0, -0);            // false
Object.is(0, +0);            // true
Object.is(-0, -0);           // true
Object.is(NaN, 0/0);         // true

# Map 原理

  • Map是利用链表,hash思想来实现的。
  • 由于不同的key值算出的hash值可能相同,所以将相同的数据放到一个链表上。
  • 链表的优势在于在中间的任意位置添加、删除元素都非常快,不需要移动其他元素,直接改变指针的指向就可以。

# Map 实例的属性及方法

  • Map.prototype.size: Map结构的成员总数
  • Map.prototype.set(key, value): 设置,返回Map实例
  • Map.prototype.get(key): 获取,如找不到,返回undefined
  • Map.prototype.has(key): 判断键是否在当前的Map结构中,返回布尔值
  • Map.prototype.delete(key): 删除,若删除成功,返回true,若删除失败,返回false
  • Map.prototype.clear(): 清空,无返回值

# Map 遍历方法

  • Map.prototype.keys():返回键名的遍历器。
  • Map.prototype.values():返回键值的遍历器。
  • Map.prototype.entries():返回所有成员的遍历器。
  • Map.prototype.forEach():遍历 Map 的所有成员。 遍历的顺序就是插入的顺序
const 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"

map.forEach(function(value, key, map) {
  console.log(key, value)
})
// "F" "no"
// "T" "yes"

// forEach 接受第二个参数,用于绑定 this
const reporter = {
    report: function(key, value) {
      console.log(key, value)
    }
}
map.forEach(function(value, key, map) {
  this.report(key, value)
}, reporter)
// "F" "no"
// "T" "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的默认遍历器接口(Symbol.iterator属性)就是entries方法。

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

# 模拟实现Map

  class MyMap {
    constructor() {
      this.init()
    }
    // 初始化函数,创建桶(数组),每个位置都是一个对象,每个对象的属性上设置next属性,并且初始化为null。
    init() {
      this.tong = new Array(8);
      for (var i = 0; i < 8; i++) {
          this.tong[i] = new Object();
          this.tong[i].next = null;
      }
    }

    // 判断key 是否相同
    isEqual(key1, key2) {
      // -0 跟 +0 是相等的
      if (key1 === 0 && key2 === 0) return true
      // 其他情况 采用 Object.is方法
      return Object.is(key1, key2)
    }

    // 创造hash值。
    hash(key) {
      let hash = 0;
      if(typeof key !== 'string'){
          if(typeof key == 'number'){  //传入的是数字或者NaN时,不是NaN时直接赋给hash;
              hash = Object.is(key, NaN) ? 0 : key;
          }else if(typeof key == 'object'){  //处理传入的是对象时: []、{}、 null等;
              hash = 1;
          }else if(typeof key == 'boolean'){ //boolean类型时,true转化为1;false转换为0;
              hash = key;
          }else{                      //剩下的 undefined function(){} 类型直接转化为2;
              hash = 2;
          }
      }else{                          //转换string形式的key;
          for(let i = 0; i < 3; i++){        //将传入的字符串转化为特定的数字;
              hash += key[i] ? key[i].charCodeAt(0) : 0;
          }
      }
      return hash % 8;    //将hash转化为[0,8)区间的值;可以分不到不同的桶里的对象中去;
    }

    // 添加数据。
    set(key, value) {
      var index = this.hash(key);        //获取到当前设置的key设置到那个位置上
      var TempBucket = this.tong[index]; //获取当前位置的对象
      while (TempBucket.next) {          //遍历如果当前对象链接的下一个不为空
          if (this.isEqual(TempBucket.next.key, key)) {  //如果要设置的属性已经存在,覆盖其值。
              TempBucket.next.value = value;
              return this;                          //return ,不在继续遍历
          } else {
              TempBucket = TempBucket.next;  //把指针指向下一个对象。
          }

      }
      TempBucket.next = {  //对象的next是null ,添加对象。
          key: key,
          value: value,
          next: null
      }
      return this
    }
    // 获取值
    get(key) {
      var index = this.hash(key);
      var TempBucket = this.tong[index];
      while(TempBucket){
          if(this.isEqual(TempBucket.key, key)){
              return TempBucket.value;
          }else{
              TempBucket = TempBucket.next;
          }
      }
      return undefined;
    }

    // 删除值
    delete(key) {
      var index = this.hash(key);
      var TempBucket = this.tong[index];
      while(TempBucket){
          if(this.isEqual(TempBucket.next.key, key)){
              TempBucket.next = TempBucket.next.next;
              return true;
          }else{
              TempBucket = TempBucket.next;
          }
      }
      return false
    }

    // 判断值
    has(key) {
      var index = this.hash(key);
      var TempBucket = this.tong[index];
      while(TempBucket){
          if(this.isEqual(TempBucket.key, key)){
              return true;
          }else{
              TempBucket = TempBucket.next;
          }
      }
      return false;
    }

    // 清空
    clear() {
      this.init()
    }
  }

var map = new Mymap();    //使用构造函数的方式实例化mapmap.set('name','zwq');map.get('name');map.has('name);

最后一次修改时间: 10/18/2020, 10:04:40 PM