# 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): 获取,如找不到,返回undefinedMap.prototype.has(key): 判断键是否在当前的Map结构中,返回布尔值Map.prototype.delete(key): 删除,若删除成功,返回true,若删除失败,返回falseMap.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);