七: ES6 Set Map

前言 该部分为书籍 深入理解ES6 第七章(Set与Map)笔记 ES5中的 Set 与 Map 在 ES5 中, 一般使用对象属性来模拟 Set 与 Map let set = Object.create(null); set.foo = true; // 检查属性的存在性 if (set.foo) { // 一些操作 } 使用对象模拟 Map 与 Set 之间唯一真正的区别是所存储的值 与 Set 不同, Map 多数被用来提取数据,而不是仅检查键的存在性。 let map = Object.create(null); map.foo = "bar"; // 提取一个值 let value = map.foo; console.log(value); // "bar" 变通方法的问题 由于对象属性的类型必须为字符串,你就必须保证任意两个键不能被转换为相同的字符串。 let map = Object.create(null); map[5] = "foo"; console.log(map["5"]); // "foo" 若使用对象作为键, 就会出现另一个问题(对象会被转换为字符串[object Object]) ES6的 Set ES6 新增了 Set 类型, 这是一种无重复值的有序列表.(Set 不是一种基础类型, 还是 Object 类型之上封装的一种数据结构, [[class]] 属性为 "Set") Set 允许对它包含的数据进行快速访问,从而增加了一个追踪离散值的更有效方式。 1. 创建 Set 使用 new Set()来创建 在 Set 内部的比较使用了 Object.is() 方法, 来判断两个值是否相等, 唯一的例外是 +0 与-0 在 Set 中被判断为是相等的 // 参数: iterable // 如果传递一个可迭代对象,它的所有元素将不重复地被添加到新的 Set中。如果不指定此参数或其值为null,则新的 Set为空。 let set = new Set([iterable]?); 2. 方法以及属性 Set.prototype.add(value): 添加项目 如果 add() 方法用相同值进行了多次调用,那么在第一次之后的调用实际上会被忽略 set.add(5); Set.prototype.delete(value): 删除值 移除Set的中与这个值相等的元素,返回Set.prototype.has(value)在这个操作前会返回的值(即如果该元素存在,返回true,否则返回false)。Set.prototype.has(value)在此后会返回false。 Set.prototype.clear():删除所有值 Set.prototype.has(value): 判断值是否存在 Set.prototype.size: 属性, 返回Set对象的值的个数。 3. 迭代方法 Set.prototype.forEach(callbackFn[, thisArg]) 因为 Set 并没有数组或者对象概念的索引或键, 为了与其他数据结构的 forEach 保持一致, 回调函数的第一个与第二个参数是相同的 要记住,虽然 Set 能非常好地追踪值,并且 forEach() 可以让你按顺序处理每一项,但是却无法像数组那样用索引来直接访问某个值。 let set = new Set([1, 2]); /* * 回调函数接受三个参数: * Set 中下个位置的值 * 与第一个参数相同的值 * 目标 Set 自身 */ set.forEach(function(value, key, ownerSet) { console.log(key + " " + value); console.log(ownerSet === set); }); Set.prototype.keys() 与values()方法相同,返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。 Set.prototype.values() 返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。 Set.prototype.entries() 返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值的[value, value]数组。为了使这个方法和Map对象保持相似, 每个值的键和值相等。 4.将 Set 转化为数组 数组转 Set 直接将数组传递给 Set 构造器 Set转 数组 使用 扩展运算符 let set = new Set([1, 2, 3, 3, 3, 4, 5]), array = [...set]; console.log(array); // [1,2,3,4,5] 可以用这个来快速进行数组去重 function eliminateDuplicates(items) { return [...new Set(items)]; } ES6中 Weak Set 由于 Set 类型存储对象引用的方式,它也可以被称为 Strong Set 。对象存储在 Set 的一个实例中时,实际上相当于把对象存储在变量中。只要对于 Set 实例的引用仍然存在,所存储的对象就无法被垃圾回收机制回收,从而无法释放内存。 let set = new Set(), key = {}; set.add(key); console.log(set.size); // 1 // 取消原始引用 // 将 key 设置为 null 清除了对 key 对象的一个引用,但是另一个引用还存于set 内部 key = null; console.log(set.size); // 1 // 重新获得原始引用 key = [...set][0]; 为了缓解这个问题, ES6 也包含了 Weak Set ,该类型只允许存储对象弱引用,而不能存储基本类型的值。对象的弱引用在它自己成为该对象的唯一引用时,不会阻止垃圾回收 1. 创建 Weak Set 使用 new WeakSet()构造器 **注意: ** Weak Set 项不能存在非对象的值, 如果传入了非对象的值, 就会抛出错误 // [iterable]: 如果传入一个可迭代对象作为参数, 则该对象的所有迭代值都会被自动添加进生成的 WeakSet 对象中。null 被认为是 undefined。 let set = new WeakSet([iterable]) 方法 WeakSet.prototype.add(value): 添加项 WeakSet.prototype.has(value): 判断项 WeakSet.prototype.delete(value): 删除项 2. 与 Set 类型的差异 Weak Set 看起来功能有限,而这对于正确管理内存而言是必要的。一般来说,若只想追踪对象的引用,应当使用 Weak Set 而不是正规 Set 。 最大区别是对象的弱引用(即当其他对象没有引用时, 就会被环境所回收) JS 引擎垃圾回收机制最常见的就是标记清除法(可参考JavaScript 高级程序设计书) 对于 WeakSet 的实例,若调用 add() 方法时传入了非对象的参数,就会抛出错误(has() 或 delete() 则会在传入了非对象的参数时返回 false ); Weak Set 不可迭代,因此不能被用在 for-of 循环中; Weak Set 无法暴露出任何迭代器(例如 keys() 与 values() 方法),因此没有任何编程手段可用于判断 Weak Set 的内容; Weak Set 没有 forEach() 方法; Weak Set 没有 size 属性。 ES6中的Map ES6的 Map 类型是键值对的有序列表, 而键和值都可以是任意类型 . 键的比较使用的是 Object.is(), 因此 5与 "5" 同时作为键, 因为它们类型不同. 1. 创建 Map new Map([iterable]) 在方法内部使用的是 Object.is() , 与 new Set() 类似 // Iterable 可以是一个数组或者其他 iterable 对象,其元素为键值对(两个元素的数组,例如: [[ 1, 'one' ],[ 2, 'two' ]])。 每个键值对都会添加到新的 Map。null 会被当做 undefined。 let set = new Set([iterable]?); 2. 方法 和 属性 Map.prototype.size: 返回Map对象的键/值对的数量。 Map.prototype.get(key): 返回键对应的值,如果不存在,则返回undefined。 Map.prototype.set(key, value): 设置Map对象中键的值。返回该Map对象。 Map.prototype.has(key): 返回一个布尔值,表示Map实例是否包含键对应的值。 Map.prototype.delete(key): 删除项 如果 Map 对象中存在该元素,则移除它并返回 true;否则如果该元素不存在则返回 false。随后调用 Map.prototype.has(key) 将返回 false 。 Map.prototype.clear(): 移除Map对象的所有键/值对 。 3. 迭代方法 Map.prototype.forEach(callbackFn[, thisArg]): 按插入顺序,为 Map对象里的每一键值对调用一次callbackFn函数。如果为forEach提供了thisArg,它将在每次回调中作为this值。 Map.prototype.keys(): 返回一个新的 Iterator对象, 它按插入顺序包含了Map对象中每个元素的键 。 Map.prototype.values():返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的值 。 Map.prototype.entries(): 返回一个新的 Iterator 对象,它按插入顺序包含了Map对象中每个元素的 [key, value] 数组。 4. 与普通对象(Object)的区别 一个Object的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值,包括函数、对象、基本类型。 Map 中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,Map 对象是按插入的顺序返回键值。 注意:自ECMAScript 2015规范以来,对象确实保留了字符串和Symbol键的创建顺序; 因此,在只有字符串键的对象上进行迭代将按插入顺序产生键。** 你可以通过 size 属性直接获取一个 Map 的键值对个数,而 Object 的键值对个数只能手动计算。 Map 可直接进行迭代,而 Object 的迭代需要先获取它的键数组,然后再进行迭代。 Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。 注意:虽然 ES5 开始可以用 map = Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见 Map 在涉及频繁增删键值对的场景下会有些性能优势。 ES6中的 Weak Map Weak Map 对 Map 而言,就像 Weak Set 对 Set 一样: Weak 版本都是存储对象弱引用的方式。在 Weak Map 中,所有的键都必须是对象(尝试使用非对象的键会抛出错误),而且这些对象都是弱引用,不会干扰垃圾回收。当 Weak Map 中的键在 Weak Map 之外不存在引用时,该键值对会被移除。 必须注意的是, Weak Map 的键才是弱引用,而值不是。在 Weak Map 的值中存储对象会阻止垃圾回收,即使该对象的其他引用已全都被移除。 1. 创建 Weak Map 使用 new WeakMap() 构造器 键必须是非空的对象, 否则会抛出错误 值则允许是任意类型 // Iterable 是一个数组(二元数组)或者其他可迭代的且其元素是键值对的对象。每个键值对会被加到新的 WeakMap 里。null 会被当做 undefined。 new WeakMap([iterable]?) 方法 WeakMap.prototype.set(key, value): 在WeakMap中设置一组key关联对象,返回这个 WeakMap对象。 WeakMap.prototype.get(key): 返回key关联对象, 或者 undefined(没有key关联对象时)。 WeakMap.prototype.has(key): 根据是否有key关联对象返回一个Boolean值。 WeakMap.prototype.delete(key): 移除key的关联对象。执行后 WeakMap.prototype.has(key)返回false。 2. Weak Map 的用法与局限性 当决定是要使用 Weak Map 还是使用正规 Map 时,首要考虑因素在于你是否只想使用对象类型的键。如果你打算这么做,那么最好的选择就是 Weak Map 。因为它能确保额外数据在不再可用后被销毁,从而能优化内存使用并规避内存泄漏。 Weak Map 值为他们的内容提供了很小的可见度, 因此你不能使用 forEach() 方法、size 属性 或 clear() 方法来管理其中的项

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):