第七章 Set 与 Map
《深入理解ES6》—— Nicholas C. Zakas
ES6之前 JS只存在一种集合类型,就是数组类型。
数组只使用了数值型的索引,
而如果非数值型的索引是必要的,开发者就会使用非数组的对象。
这种技巧引入了非数组对象的定制实现,即 Set 与 Map。
Set 是不包含重复值的列表。Map则是键与相对应的值的集合。
1. ES5中的 Set 与 Map
1.1. 用对象模拟
let set = Object.create( null );
let map = Object.create( null );
set
和map
的原型对象是 null
,确保在此对象上没有继承属性。
set
只关注键不重复。
map
关注值。
1.2. 问题
- key只能是字符串
- 检查属性的存在性的问题:
if ( map.key1 ) { ... }
2. ES6 的 Set
ES6新增了 Set
类型,这是一种无重复值的有序列表。
2.1. 使用
// 创建
let set = new Set();
// 添加项目
set.add( 5 );
set.add( "5" );
// 获取项目总数
set.size; //=> 2
// 用数组初始化
set = new Set( [ 1, 2, 3, 3, 3] );
set.size; //=> 3
// 判断是否存在某值
set.has( 1 ); //=> true
- Set内部通过
Object.is()
判断是否相等
2.2. 移除值
let set = new Set( [ 5, "5" ] );
// 删除单个值
set.delete( 5 );
set.size; //=> 1
// 清空
set.clear();
set.size; //=> 0
2.3. 遍历
let set = new Set( [ 1, 2] );
// value:值
// key:Set没有key,其值与value一致
// ownerSet:目标 Set
set.forEach( function( value, key, ownerSet ) {
console.info( value, Object.is( value, key ) );
} );
2.4. 转换为数组
// 可用于去重
let set = new Set( [ 1, 2, 2, 3 ] );
let array = [...set];
array; //=> [ 1, 2, 3 ]
2.5. WeakSet
如果值是对象的引用,但该对象(比如DOM)被移除后:
- Set 仍旧存储该引用,这会造成内存泄露
- WeakSet 不会再存储该引用,它只允许存储对象的弱引用
WeakSet 只允许存储对象的引用。
3. ES6 的 Map
ES6的 Map 类型是键值对的有序列表,键的比较使用的是 Object.is()
。
3.1. 使用
let map = new Map();
// 添加/设置 项目
map.set( "name", "吴钦飞" );
map.set( "gender", "男" );
// 获取项目
map.get( "name" ); //=> "吴钦飞"
3.2. 方法
has( key )
:判断指定的键是否存在于 Map 中delete( key )
:移除 Map 中对应的键及值clear()
:清空
3.3. 初始化
// 一维数组的每一项为 Map 中的一个键值对
let map = new Map( [
// 第一个元素为 键,第二个元素为 值
[ "name", "吴钦飞" ],
[ "gender", "男" ]
] );
map.get( "name" ); //=> "吴钦飞"
3.4. 遍历
let map = new Map( [
[ "name", "吴钦飞" ],
[ "gender", "男" ]
] );
map.forEach( function( value, key, ownerMap ) {
console.info( key + " " + value );
} );
3.5. WeakMap
- 所有键必须是对象,且键都是弱引用
- 不会干扰垃圾回收,当键在 WeakMap之外不存在引用时,该键值会被移除
- 适合在浏览器中创建一个关联到特定DOM元素的对象。
某些用在网页上的JS库会维护一个自定义对象,
用于引用该库所使用的每一个DOM元素,
并且其映射关系会存储在内部的对象缓存中。
如何判断一个DOM元素已不复存在于网页中,
以便该库能移除此元素的关联对象。
若做不到,该库就会继续保持对DOM元素的一个无效引用,并造成内存泄露。
私有数据:
let Person = ( function() {
let privateData = new WeakMap();
function Person( name ) {
privateData.set( this, { name: name } );
}
Person.prototype.getName = function() {
return privateData.get( this ).data;
}
} )();