JavaScriptES6Map与WeakMap

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/c__dreamer/article/details/82182649

目录

 

Map

含义与用法

实例的属性和操作方法

遍历方法

WeakMap

含义

WeakMap的语法

WeakMap的用途


Map

含义与用法

 JavaScript的对象,本质上是键值对的集合(hash结构),但是传统上只能用字符串作为建,这有了很大的限制。

const data = {};
const element = document.getElementsByTagName("div")[0];
data[element] = "metadata";
console.log(data['[object HTMLDivElement]']);       //metadata
//上面代码原意是将一个Dom节点作为对象data的键,但是由于对象只接受字符串作为键名,所以element被自动的转为了字符串。

 Es6提供了Map数据结构,它类似于对象,也是键值对的集合,但是‘键’的范围不限于字符串,各种类型的值(包括对象)都可以作为键名。也就是说,Object结构提供了“字符串-值”的对应,Map结构提供了“值-值”的对应。是一种更完善的Hash结构实现。如果你需要‘键值对’的数据结构,Map比Object更适合。

 

const m = new Map();
const o = {
     p : "Hello World",
}
m.set(o,"content");
console.log(m.get(o));  //content
console.log(m.has(o));  //true
m.delete(o);            
console.log(m.has(o));  //false

 Map作为构造函数,也是可以接受一个数组作为参数。该数组的成员表示键值对的数组。

const map = new Map([
     ["name","张三"],
     ["title","Author"],
])
console.log(map.get('name'));   //张三
console.log(map.get("title"));      //Author
console.log(map);

 Map构造函数接受数组作为参数,实际上执行的是以下算法。

const items = [
     ["name","张三"],
     ["title","Author"],
]
const map = new Map();
items.forEach(([key,value]) => map.set(key,value));
console.log(map);

 事实上,不仅是数组,任何具有Iterator接口,且每个成员都是一个双元素的数组的数据结构都可以当做Map构造函数的参数。这就是说,Set和Map都可以用来生成新的Map。

const set = new Set([
     ["foo",1],
]);
const m1 = new Map(set);
console.log(m1);
const m2 = new Map([
    ["bar",2],
])
const m3 = new Map(m2);
console.log(m3);

 在Map结构中,如果对同一个值复制多次,后面的值将覆盖前面的值。

const map = new Map();
 map 
    .set(1,"aaa")
    .set(1,"bbb");
console.log(map.get(1));        //bbb  aaa被覆盖

 如果读取一个未知的键,则返回undefined。

console.log(new Map().get("wang")); //undefined

 注意:只有对同一个对象的引用,Map结构才将其视为同一个键。引用值由于存储地址不同,相同的会被视为两个值。

const map = new Map();
map.set([123],"123");
console.log(map.get([123]));        //undefined

 同理,同样的值的两个实例,在Map结构中被视为两个键。

const map = new Map();
const k1 = ['a'];
const k2 = ['a'];
map.set(k1,111);
map.set(k2,222);
console.log(map.get(k1));       //111
console.log(map.get(k2));       //222

 如果Map的键是一个原始数据类型,只要两个键严格相同,就视为是同一个键。

const map = new Map();
map.set(-0,1);
console.log(map.get(+0));   //1
map.set(true,2);
map.set('true',3);
console.log(map.get(true));     //2
map.set(undefined,4);
map.set(null,5);
console.log(map.get(undefined));        //4
map.set(NaN,6);
console.log(map.get(NaN));          //6

实例的属性和操作方法

 size属性

 size属性返回Map结构的成员总数。

const map = new Map();
map.set("foo",1).set('bar',2);
console.log(map.size);  //2

 set(key,value)

 set方法设置键名key对应的键值value,然后返回整个Map结构,如果key已经有值,则键值会被更新,否则就新生成该键。

const m = new Map();
m.set("edition",6);
m.set(123,"standard");
m.set(undefined,'nah');

 set方法返回的是当前的Map对象,因此可以采用链式写法。

const map = new Map();
map.set("foo",1).set("bar",2).set("baz",3);
console.log(map);

 get(key)

 get方法读取key对应的键值,如果找不到key,返回undefined。

const map = new Map();
const hello = () => {
    console.log("hello");
}
map.set(hello,"Hello ES6");
console.log(map.get(hello));    //Hello ES6

 has(key)

 has方法返回一个布尔值,表示某个键是否在当前Map对象中。

const map = new Map();
map.set("foo",1);
console.log(map.has("foo"));    //true
console.log(map.has("bar"));    //false

 delete(key)

 delete方法删除某个键,返回true,如果删除失败,返回false。

const map = new Map();
map.set("foo",1);
console.log(map.delete("foo")); //true
console.log(map.delete("foo")); //false

 clear()

 clear方法清除所有成员,没有返回值。

const map = new Map();
map.set("foo",1).set("baz",2);
console.log(map.size);  //2
map.clear();
console.log(map.size);  //0

遍历方法

 Map结构原生提供是三个遍历器生成函数和一个遍历方法。

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

 注意:Map的遍历顺序是插入顺序。

const map = new Map([
     ["foo",1],
     ["bar",2],
])
for(let key of map.keys()){
    console.log(key);
}
for(let value of map.values()){
     console.log(value);
}
for(let items of map.entries()){
    console.log(items);
}

 Map结构的默认遍历器接口就是entries方法。

const map = new Map(); 
console.log(map[Symbol.iterator] === map.entries);  //true

 Map结构转为数组可以用扩展运算符。

const map = new Map([
    [1,"foo"],
    [2,"bar"],
    [3,"baz"]
])
console.log([...map.keys()])    //[1,2,3]
console.log([...map.values()]); //['foo','bar','baz']
console.log([...map.entries()]);    //[[1,'foo'],[2,'bar'],[3,'baz']]

 结合数组的map方法,filter方法,可以实现Map的遍历和过滤。

const map = new Map([
     [1,'a'],
     [2,'b'],
     [3,'c'],
]);
const map1 = new Map([...map].filter(([k,v])=> k < 3));
console.log(map1);
const map2 = new Map([...map].map( ([k,v]) =>[ k * 2,v]));
console.log(map2);

 Map有一个forEach方法,与数组的forEach方法类似,也可以实现遍历,还可以接受第二个参数,用来绑定this。

const map = new Map();
map.set(1,"for").set(2,"bar").set(3,"baz");
map.forEach( (value,key,map) => {
     console.log(key,value);
})
const reporter = {
    report(key,value) {
        console.log("Key %s","Value %s",key,value);
    }
}
map.forEach( function (value,key,map)  {
    this.report(key,value);
},reporter);

WeakMap

含义

 Weakmap结构与Map结构类似,也是用于生成键值的集合。

 WeakMap可以使用Set方法添加成员。

const wm1 = new WeakMap();
const key = {foo : 1};
wm1.set(key,2);
console.log(wm1.get(key));      //2

 WeakMap 也可以接受一个数组,作为构造函数的参数。

const k1 = [1,2,3];
const k2 = [4,5,6];
const wm = new WeakMap([[k1,"foo"],[k2,"bar"]]);
console.log(wm.get(k2));    //bar

WeakMap与Map有两点区别。

第一:和WeakSet一样,WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
第二:WeakSet的键名所指的对象,不计入垃圾回收机制。

 WeakMap的设计目的在于,有时我们想在某个对象上面存放一些数据,但是这会形成对于这个对象的引用。一旦不再需要这两个对象,我们就必须手动删除这个引用,否则垃圾回收机制就不会释放对象占用的内存。

const e1 = document.getElementById("foo");
const e2 = document.getElementById("bar");
const arr = [
    [e1,"foo元素"],
     [e2,"bar元素"],
]
//不再使用必须手动删除引用
arr[0] = null;
arr[1] = null;

 WeakMap 就是为了解决这个问题而诞生的,它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。

 基本上,如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap。一个典型应用场景是,在网页的 DOM 元素上添加数据,就可以使用WeakMap结构。当该 DOM 元素被清除,其所对应的WeakMap记录就会自动被移除。

const wm = new WeakMap();
const element = document.getElementsByTagName("div")[0];
wm.set(element,"some information");
console.log(wm.get(element));

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

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

WeakMap的语法

 WeakMap和WeakSet相似,没有遍历方法和清空方法,WeakMap只有四个方法可以使用:get(),set(),has(),delete()。

const wm = new WeakMap();
let key = {};
wm.set(key,"123");
console.log(wm.get(key));   //123
wm.delete(key);
console.log(wm.get(key));   //undefined
console.log(wm.size)    //undefined
console.log(wm.forEach) //undefined
console.log(wm.clear)   //undefined

WeakMap的用途

 WeakMap应用的典型场合就是Dom节点作为键名。

let myElement = document.getElementsByTagName('div')[0];
let myWeakMap = new WeakMap();
myWeakMap.set(myElement,{timesClicked: 0});
myElement.addEventListener('click',function(){
    let logoData = myWeakMap.get(myElement);
    console.log(logoData.timesClicked ++);
},false)
//上面代码中,myElement是一个 DOM 节点,每当发生click事件,就更新一下状态。我们将这个状态作为键值放在 WeakMap 里,
//对应的键名就是myElement。一旦这个 DOM 节点删除,该状态就会自动消失,不存在内存泄漏风险。

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

const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
     constructor(counter,action) {
         _counter.set(this,counter);
         _action.set(this,action);
    }
     dec(){
         let counter = _counter.get(this);
         if(counter < 1){
             return ;
         }
        counter --;
        _counter.set(this,counter);
        if(counter === 0){
            _action.get(this)();
         }
     }
}
const c = new Countdown(2,() => console.log('DOWM'));
c.dec();
c.dec();    //DOWN

 上面代码中,Countdown类的两个内部属性_counter和_action,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏。

主页传送门

展开阅读全文

没有更多推荐了,返回首页