JS 中的 (Weak)Set 和 (Weak)Map

栏目: JavaScript · 发布时间: 6年前

SetMap 都是 ES6 新增数据结构。

Set

Set 是一个集合,它类似于数组,但是成员的值都是唯一的,没有重复的值。它允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

Set 是一个构造函数,它有一个可选的参数一个可迭代对象。如果传递了这个参数它的所有元素将不重复地被添加到新的 Set 中。如果不指定此参数或其值为 null ,则新的 Set 为空。它返回一个新的 Set 实例。

let s1 = new Set([NaN, undefined, null, +0, -0, Infinity, -Infinity, NaN, undefined, null])

console.log(s1)
/*
Set(6) {NaN, undefined, null, 0, Infinity, …}
    size: 6
    __proto__: Set
    [[Entries]]: Array(6)
        0: NaN
        1: undefined
        2: null
        3: 0
        4: Infinity
        5: -Infinity
        length: 6
*/
// 可以发现在 Set 中 NaN 之间被视为相同的值
复制代码

Set 常用于去除重复元素。

// 去除数组的重复成员
[...new Set([1,2,3,1,2,3])]

// 去除重复字符
[...new Set('abcabc')].join('')
复制代码

Set 原型属性

除了 constructorSet.prototype 上还有一个 size 属性,它返回 Set 对象值的个数。

Set 原型方法

Set 一共有 9 个原型方法。

add(value)

用来向一个 Set 对象的末尾添加一个指定的值,它返回 Set 对象本身。

let s2 = new Set([1])
s2.add(2).add(3) // { 1, 2, 3 }
复制代码

has(value)

返回一个布尔值来指示对应的值 value 是否存在 Set 对象中。

delete(value)

可以从一个 Set 对象中删除指定的元素,成功删除返回 true ,否则返回 false

clear()

用来清空一个 Set 对象中的所有元素,返回 undefiend

values()

返回一个 Iterator 对象,这个对象以插入 Set 对象的顺序包含了原 Set 对象里的每个元素。

let s3 = new Set([1,2,3])
console.log(s3.values())
// SetIterator {1, 2, 3}
复制代码

keys()

values() 的别名,功能和 values() 完全相同。

entries()

返回一个新的迭代器对象 ,这个对象的元素是类似 [value, value] 形式的数组。

console.log(s3.entries()) // SetIterator {1, 2, 3}
for (let i of s3.entries()) {
    console.log(i)
}
/*
[1, 1]
[2, 2]
[3, 3]
*/
复制代码

forEach(callback[, thisArg])

根据集合中元素的顺序,对每个元素都执行提供的 callback 函数一次。它接受两个参数第一个是回调函数,第二个是回调函数的 this 指向(可选)。

(new Set([5,2,false, [], {}])).forEach(function (value, index, obj) {
    console.log(value, index, obj)
    // value 元素
    // index 元素索引等于 value
    // obj set 对象
})
/*
5 5 Set(5)
2 2 Set(5)
false false Set(5)
[] [] Set(5)
{} {} Set(5)
*/
复制代码

[@@iterator]()

Set 的迭代器函数,默认是 values 函数。

console.log(s3.[Symbol.iterator])
// ƒ values() { [native code] }
复制代码

Set 静态属性

  1. Set.length 它值为 0
  2. Set[Symbol.species] 返回 Set 构造函数
  3. Set.prototype 原型

WeakSet

WeakSetSet 类似,但是有两点不同:

  1. WeakSet 对象中只能存放对象引用, 不能存放值, 而 Set 对象都可以.
  2. WeakSet 对象中存储的对象值都是被弱引用的, 如果没有其他的变量或属性引用这个对象值, 则这个对象值会被当成垃圾回收掉. 正因为这样, WeakSet 对象是无法被枚举的, 没有办法拿到它包含的所有元素.
const ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set
复制代码

WeakSet 原型属性

WeakSet 只有 constructor 属性

WeakSet 原型方法

WeakSet 只有 3 个原型方法, add has delete 。它没有迭代相关的方法和 clear 方法。

WeakSet 静态属性

对比 Set 它少了 Symbol.species 属性。

Map

Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。它和 JS 对象不同,JS 对象只能用字符串和Symbol作为键,而 Map 可以使用任何值。

除了键类型上的不同,它和 Object 还有以下不同:

  1. Map 中的键值是有序的,而添加到对象中的键则不是。
  2. Map 可以通过 size 获取键值对个数,而 Object 的键值对个数只能手动计算。
  3. Map 可直接进行迭代,而 Object 的迭代需要先获取它的键数组,然后再进行迭代。
  4. Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。虽然 ES5 开始可以用 map = Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。
  5. Map 在涉及频繁增删键值对的场景下会有些性能优势。
const data = {};
const element = document.getElementById('myDiv');

data[element] = 'metadata';
data['[object HTMLDivElement]'] // "metadata"

// -----------------------

var myMap = new Map();
myMap.set(NaN, "not a number");

myMap.get(NaN); // "not a number"

var otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"
//  NaN 作为 Map 的键来说是没有区别的
复制代码

Map 是一个构造函数,它接受一个可选的参数,可以是一个数组或者其他 iterable 对象,其元素或为键值对,或为两个元素的数组。 每个键值对都会添加到新的 Map

let m1 = new Map([[1,2], ['key', 'value'], [false, undefined], [null, null], [undefined, 1], [undefined, undefined]])
console.log(m1)
// Map(5) {1 => 2, "key" => "value", false => undefined, null => null, undefined => undefined}
复制代码

Map 是可以直接被迭代的,一个 Map 对象在迭代时会根据对象中元素的插入顺序来进行 — 一个 for...of 循环在每次迭代后会返回一个形式为 [key,value] 的数组。

var myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
for (var [key, value] of myMap) {
  console.log(key + " = " + value);
}
// 将会显示两个log。一个是"0 = zero"另一个是"1 = one"

// 合并两个 Map
var first = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

var second = new Map([
  [1, 'uno'],
  [2, 'dos']
]);

// 合并两个Map对象时,如果有重复的键值,则后面的会覆盖前面的。
// 展开运算符本质上是将Map对象转换成数组。
var merged = new Map([...first, ...second]);
复制代码

Map 的原型属性

除了 constructorMap 原型上还有一个 size 属性,它返回 Map 对象键值对的数量。

Map 的原型方法

Map 一共有 10 个原型方法。

set(key, value)

Map 对象添加或更新一个指定了键(key)和值(value)的(新)键值对。它返回 Map 对象。

let m2 = new Map()
m2.set(1,2).set(2,3).set(2,100)
console.log(m2)
// Map(2) {1 => 2, 2 => 100}
复制代码

get(key)

返回键对应的值,如果不存在,则返回 undefined

has(key)

如果指定元素存在于 Map 中,则返回 true 。否则返回 false

delete(key)

移除 Map 对象中指定的元素,如果 Map 对象中存在该元素,则移除它并返回 true ;否则如果该元素不存在则返回 false

clear()

移除 Map 对象中的所有元素。返回 undefined

keys()

返回一个新的Iterator对象。它包含按照顺序插入 Map 对象中每个元素的 key 值。

var myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");

var mapIter = myMap.keys();

console.log(mapIter.next().value); // "0"
console.log(mapIter.next().value); // 1
console.log(mapIter.next().value); // Object
复制代码

values()

返回一个新的 Iterator 对象。它包含按顺序插入 Map 对象中每个元素的 value 值。

entries()

返回一个新的包含 [key, value] 对的 Iterator 对象,返回的迭代器的迭代顺序与 Map 对象的插入顺序相同。它就像直接迭代 Map 对象一样。

forEach(callback[, thisArg])

将会以插入顺序对 Map 对象中的每一个键值对执行一次参数中提供的回调函数。第二参数是回调函数的 this 指向,返回 undefined

第一个参数回掉函数的参数为:

value
key
Map
function logMapElements(value, key, map) {
    console.log("m[" + key + "] = " + value);
}
Map([["foo", 3], ["bar", {}], ["baz", undefined]]).forEach(logMapElements);
// logs:
// "m[foo] = 3"
// "m[bar] = [object Object]"
// "m[baz] = undefined"
复制代码

[@@iterator]()

@@iterator 属性的初始值与 entries 属性的初始值是同一个函数对象。

var myMap = new Map();
myMap.set('0', 'foo');
myMap.set(1, 'bar');
myMap.set({}, 'baz');

var mapIter = myMap[Symbol.iterator]();
//返回的其实是个generator
console.log(mapIter.next().value); // ["0", "foo"]
console.log(mapIter.next().value); // [1, "bar"]
console.log(mapIter.next().value); // [Object, "baz"]
复制代码

Map 和 JSON

Map 不能使用 JSON.stringify 转换为 json ,如果是字符串键的话,可以先将它转化为 object ,再转化为 json ,或者直接转化为数组 json

function mapToArrayJson(map) {
  return JSON.stringify([...map]);
}

let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'
复制代码

Map 静态属性

  1. Map.length 值为 0
  2. Map[Symbol.species] 返回一个 Map 构造函数,一般用于创建派生对象。
  3. Map.prototype 原型

WeakMap

WeakSet 对象允许你将弱保持对象存储在一个集合中。

它和 Map 类似,但有两点不同:

  1. WeakSet 对象中只能存放对象引用, 不能存放值, 而 Set 对象都可以。
  2. WeakSet 对象中存储的对象值都是被弱引用的, 如果没有其他的变量或属性引用这个对象值, 则这个对象值会被当成垃圾回收掉. 正因为这样, WeakSet 对象是无法被枚举的, 没有办法拿到它包含的所有元素.

WeakMap 原型属性

它只有一个 constructor 属性。

WeakMap 原型方法

WeakMap 有 5 个原型方法( set get has delete clear ),相比 Map 它少了迭代类型的方法和 clear 方法。

WeakMap 静态方法

对比 Map ,它少了 Symbol.species 属性。

WeakMap 的用途

WeakMap 一般将 DOM 节点作为键,这样 DOM 节点被删除了, WeakMap 中也会消失,这样久不存在内存泄漏。

let myElement = document.getElementById('logo');
let myWeakmap = new WeakMap();

myWeakmap.set(myElement, {timesClicked: 0});

myElement.addEventListener('click', function() {
  let logoData = myWeakmap.get(myElement);
  logoData.timesClicked++;
}, false);
复制代码

以上所述就是小编给大家介绍的《JS 中的 (Weak)Set 和 (Weak)Map》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

图形程序开发人员指南

图形程序开发人员指南

Michael Abrash / 前导工作室 / 机械工业出版社 / 1998 / 128

Michael Abrash's classic Graphics Programming Black Book is a compilation of Michael's previous writings on assembly language and graphics programming (including from his "Graphics Programming" column......一起来看看 《图形程序开发人员指南》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具