小酌ObjectMap和WeakMap

Posted 恪愚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小酌ObjectMap和WeakMap相关的知识,希望对你有一定的参考价值。

Object

对于普通Object,它的优势在于存、取元素,使用非常方便,可直接用字面量方式创建;而且里面属性值可以是不同类型:

let obj=;
obj.name='mxc';
obj.age=18;
console.log(obj);

但这不是今天的重点。
对于 Object,删除其中的属性有两种方式:delete 操作符和 =undefined

delete

delete 会从某个对象上移除指定属性。成功为 true,否则为false:

delete obj.age;

但这有几点不足之处:

  • 如果你试图删除不存在的属性,delete 不会起任何作用,但仍返回true
  • delete 不能删除全局属性/函数
  • delete 不能删除内置对象的属性
  • delete 只删除自身属性,而不管原型链上的同名属性 —— 这会导致一些问题:属性被删除成功但仍然可访问
console.log(obj);
delete obj.sex;
delete obj.valueOf();
obj.__proto__.age=19;
console.log(obj);
console.log(obj.age);

在数组中使用 delete 时,它会将某一项删掉,但会留下一个“空位”。即:delete 不会改变数组长度。

let arr=[1,2,3,4,5];
delete arr[2];
console.log(arr);

=undefined

如果你小心使用,它的效率是 delete 的100倍(V8之后做了优化)。但它却并不是这个问题的正解!笔者猜测是因为“undefined”的特殊性:

obj.name="undefined";
console.log(obj);

undefined 被认为是一个存在的值,它本身也可以用作属性名存在。

Map

es6提出的 Map 在一些情况下确实是不错的选择:Map是一种存储着许多键值对的有序列表,其中的键名和对应值支持所有的数据类型。
Map中键名的等价性是通过调用 Object.is() 来实现的 —— 它可以被看做“更严格的==、和更宽松的===”。比如数字5和字符“5”就会被判定为两个不同的键、而NaNNaN也会被判定为两个不同的键。这与对象不太一样,因为对象中的属性名总是会被强制性转为字符串类型

Map中的常用方法有:

  • set(key,value):向集合中添加新元素
  • get(key):从集合中获取信息
  • has(key):控制指定的键名在 Map 中是否已经存在
  • delete(key):从集合中移除指定的键和对应值
  • clear():移除集合中所有键值对
  • size:获取集合长度

比较有意思的是:Map中的has()方法应该是采用了类似数组的of操作符、字符串的hasOwnProperty API的原理,它只会判断自身属性!

因为在JS中“万物皆对象”,Map 的基础也是 Object!
所以你可以看到:

let map=new Map();
map.set(1,'1');
map.set('1','1-1');
map.set('age',18);
console.log(map.size);
map.delete('age');
map.has('age');
console.log(map);

但是因为上面在对obj操作时我们做了这样的一步:obj.__proto__.age=19;
所以:

而且当我们定义了一个新变量:

这更加说明了一点:js是基于原型的,在js中相同的复杂类型一定都有一个共同的“父类”!
所以:

console.log(map.age);

这一点值得“警惕”!

Map优于普通 Object 的另一点是:在 Map 中存放的键,都是按照存入顺序排列的,而Object则会根据一套规则进行排序:

  • 非负整数有限,顺序是从小到大;
  • 然后是字符串、负整数、浮点数,顺序是插入的顺序;
  • 最后是 Symbol,顺序是插入的顺序;

Object与Map的比较:
何时使用Map —— 存储的键不是字符串/数字/Symbol时选择Map,因为Object并不支持;要存储大量数据或需要进行许多增/删操作时选择Map,因为速度更快;需要保持插入顺序时选择Map,因为Object会改变;需要迭代/遍历时选择Map,因为它默认是可迭代对象;
何时使用Object —— 只是简单的数据结构或数据量比较小时选择Object,因为数据少时它占用内存更少,且新建效率更高;需要用到JSON进行文件传输时选择Object,因为JSON默认不支持Map;需要覆盖原型上的键时选择Object;

WeakMap

它是弱引用Map集合,也用于存储对象的弱引用。WeakMap集合中的键名必须是一个对象!

如果弱引用之外不存在其他的强引用时,引擎的垃圾回收机制会自动回收这个对象!

按照这个特性,它可以被用来“跟踪”DOM元素,这样在DOM元素消失时,可以自动销毁集合中的相关对象。

let map=new WeakMap(),
	ele=document.querySelector('.ele');
map.set(ele,"ori");
let value=map.get(ele);
console.log(value);   // "ori"

// 删除元素后
ele.parentNode.removeChild(ele);
ele=null;

console.log(map);  // 空:Map(0) 

除此之外,我们还可以利用它来创建一个真正的“私有属性”:

let Person=(function()
	let Data=new WeakMap();
	function Person(name)
		Data.set(this,name:name);
	
	Person.prototype.getName=function()
		return Data.get(this).name;
	;
	return Person;
)();

采用 weakMap 集合来存放私有数据。由于Person对象的实例可以直接作为集合的键使用,无须单独维护一套ID的体系来跟踪数据。只要对象实例被销毁,相关信息也被销毁,从而保证了信息的私有性。
类似es6的 class,我们通过暴露get方法完成对属性的访问。

此外,还可以根据weakMap的这个特性去做“缓存函数”。比如之前在笔者公司的项目中我是这么做的:

// 缓存函数
memorizeData(fn)
  const cache = new WeakMap();
  return function (...args) 
    return (cache.has(args) && (Date.now() - cache.get(args).time < 60000 || args = null)) && cache.get(args).dataFn || cache.set(args,dataFn: fn.apply(fn,args), time: Date.now())
  
,

以上是关于小酌ObjectMap和WeakMap的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 中的 Map, Set, WeakMap, WeakSet

JavaScript WeakMap

JavaScript WeakMap

javascript中的WeakMap和WeakSet

Map 和 WeakMap 数据结构

浅析 Map 和 WeakMap 区别以及使用场景