ES6 - 基础学习(10): Set 和 WeakSet 数据结构

Set 数据结构

ES6 提供了新的数据结构 Set,注意不是数据类型,而是数据结构。它类似于数组,但是成员的值(可以是任何类型,无论是原始值或者是对象引用都行)都是唯一的,没有重复的值。

Set 中的特殊值

Set 数据结构存储的成员 值总是唯一的,所以Set数据结构在添加新成员时需要判断两个值是否恒等,有几个特殊值需要特殊对待:

  1、+0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复;

  2、undefined 与 undefined 是恒等的,所以不重复;

  3、NaN 与 NaN 是不恒等的,但是在 Set 中只能存一个,不重复(Set 内部认为两个NaN是相等的)。

  4、两个数组或两个对象 不同引用 不恒等,即便两个数组或两个对象值完全相同不恒等(不同引用 它们存储位置不一样),故能重复

Set 数据结构基本用法

// Set本身是一个构造函数,用来生成 Set数据结构实例。
let testSet = new Set();
testSet.add(1);                        // Set(1) {1}    在数组Array时,用 unshift()push()进行值的添加;Set 稍有不同,它用更语义化的 add()进行添加
testSet.add(5);                        // Set(2) {1, 5}
testSet.add(5);                        // Set(2) {1, 5}  这里体现了值的唯一性
testSet.add("testSet");                // Set(3) {1, 5, "testSet"}  这里体现了存储类型的多样性
testSet.add(true);                     // Set(4) {1, 5, "testSet", true}
testSet.add(null);                     // Set(5) {1, 5, "testSet", true, null}
testSet.add(undefined);                // Set(6) {1, 5, "testSet", true, null, undefined}
testSet.add(undefined);                // Set(6) {1, 5, "testSet", true, null, undefined}        undefined undefined恒等,不能重复,只能添加一个
testSet.add([1, 2, 3]);                // Set(7) {1, 5, "testSet", true, null, undefined, [1,2,3]}
testSet.add([1, 2, 3]);                // Set(8) {1, 5, "testSet", true, null, undefined, [1,2,3]}    对象之间不同引用 不恒等,即使两个对象完全相同,Set也能存储
testSet.add({a: 1, b: 2});             // Set(9) {1, 5, "testSet", true, null, undefined, [1,2,3], {a: 1, b: 2}}
testSet.add({a: 1, b: 2});             // Set(10) {1, 5, "testSet", true, null, undefined, [1,2,3], {a: 1, b: 2}}    对象之间不同引用 不恒等,即使两个对象值完全相同,Set也能存储
console.log(testSet);                  // Set(8) {1, 5, "testSet", true, null, …}
// Set和 Array的区别:Set不允许内部有重复的值,如果有,则只显示一个,相当于做了去重。虽然 Set很像数组,但不是数组。
// Set构造函数可以接受一个数组字符串具有 iterable接口的其他数据结构作为参数,用来初始化 Set数据结构。
// Array 转 Set
let arrayToSet = new Set(["value1", "value2", "value3"]);
console.log(arrayToSet);                          // Set(3) {"value1", "value2", "value3"}
// String 转 Set
let stringToSet = new Set('TestDemo');
console.log(stringToSet);                          // Set(7) {"T", "e", "s", "t", "D", "m", "0"}

// Set 对象作用
// 去除数组重复成员
let deduplication = new Set([1, 2, 3, 4, 3, 2, '4']);    // Set数据结构不会做数据类型转换,所以 4和 "4"是两个不同的值
console.log(deduplication);                        // Set(5) {1, 2, 3, 4, "4"}
// 去除字符串重复字符
let deduplicationChar = new Set('TestTestTest');
console.log(deduplicationChar);                    // Set(4) {"T", "e", "s", "t"}

// 注:join方法不能格式化 Set数据结构,同时 toString方法不能将 Set数据结构转换成 String。
console.log(deduplicationChar.join());             // Uncaught TypeError: deduplicationChar.join is not a function
console.log(deduplicationChar.toString());         // [object Set]

Set 实例的属性和方法

Set 实例具有以下原型属性:  

  1、Set.prototype.constructor:构造函数,即默认的 Set函数。

  2、Set.prototype.size:返回 Set实例的成员数量总数。

Set 实例具有以下原型方法,主要分为:操作方法(用于操作数据) 和 遍历方法(用于遍历成员)。

  1、Set.prototype.add(value):添加某个值,返回 Set实例本身。

  2、Set.prototype.delete(value):删除当前 Set实例内某个值,返回一个布尔值,表示该值删除是否成功。

  3、Set.prototype.has(value):返回一个布尔值,表示该值是否为当前 Set实例内成员。

  4、Set.prototype.clear():清除所有成员(清空当前 Set实例),没有返回值。

  5、Set.prototype.keys():返回键名的遍历器。

  6、Set.prototype.values():返回键值的遍历器。

  7、Set.prototype.entries():返回键值对的遍历器。

  8、Set.prototype.forEach():遍历每个成员的回调函数,可以遍历当前 Set实例内每个成员。

  

Set 实例原型方法 - 操作方法:add(value)、delete(value)、has(value)、clear()
// add(value)
let addSet = new Set([1,'Test']);
addSet.add(NaN);
console.log(addSet);                         // Set(3) {1, "Test", NaN}
addSet.add(null).add(undefined);             // add可以链式操作,连续添加
console.log(addSet);                         // Set(5) {1, "Test", NaN, null, undefined}

// delete(value)
let deleteState = addSet.delete(null);
console.log(deleteState);                    // true
console.log(addSet.delete(0));               // false
console.log(addSet);                         // Set(4) {1, "Test", NaN, undefined}

// has(value)
let hasValue = addSet.has(NaN);
console.log(hasValue);                       // true
console.log(addSet.has(0));                  // false

// clear()
let clearSet = addSet.clear();
console.log(clearSet);                       // clear方法没有返回值,所以是 undefined
Set 实例原型方法 - 遍历方法:keys()、values()、entries()、forEach()
// Set的遍历顺序就是值的添加顺序,所以 Set数据结构的顺序是固定的,因此这个特性可以用来保存 流程化操作的回调函数,在 Promise中非常有用,用 Set实例保存一个回调函数列表,Promise就可以按照 Set实例内成员排列顺序 进行顺序调用执行。
// 由于 Set数据结构(类似数组)没有键名,只有键值(或者说键名和键值是同一个值),所以 keys方法 和 values方法的行为完全一样。
let testSet = new Set([1, "Test", NaN]);

// keys()
for (let item of testSet.keys()) {
    console.log(item);
}
// 1
// Test
// NaN

// values()
for (let item of testSet.values()) {
    console.log(item);
}
// 1
// Test
// NaN

// entries():entries方法返回的遍历器都是以数组的形式呈现,同时包括键名和键值(前是名后是值),只是两个成员完全相等。
for (let item of testSet.entries()) {
    console.log(item);
}
// [1, 1]
// ["Test", "Test"]
// [NaN, NaN]

// Set 数据结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法。这就意味着,values方法可以省略,直接用for...of循环遍历 Set实例。
for (let item of testSet) {
    console.log(item);
}
// 1
// Test
// NaN

// forEach():Set实例与数组一样,也拥有forEach方法,可用于对每个成员执行指定操作,没有返回值。
testSet.forEach(function (value, key) {
    console.log(key + ':' + value);
});
// 1:1
// Test:Test
// NaN:NaN

// forEach方法的参数是一个处理函数,该函数的参数与数组的forEach一致,依次为键值、键名、集合本身(上例省略了该参数)。注意:Set数据结构键名就是键值(两者完全相同),因此第一个参数和第二个参数的值永远是一样的。
// 另外,forEach方法还可以有第二个参数,表示绑定处理函数内部的this对象。

WeakSet 数据结构

WeakSet 数据结构 与 Set 数据结构类似,也是不重复的值的集合,但它与 Set 有两个区别。

  1、WeakSet 的成员只能是对象,而不能是其他类型的值。

  2、WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象是否还存在于 WeakSet之中。

    这是因为垃圾回收机制依赖引用计数,如果一个值的引用次数不为 0,垃圾回收机制就不会释放这块内存。结束使用该值之后,有时会忘记取消引用,从而会导致内存无法释放,进而可能会引发内存泄漏。

    WeakSet里面的引用,都不计入垃圾回收机制,所以就不存在这个问题。因此,WeakSet适合存放临时对象,以及存放跟对象绑定的信息,只要这些对象在外部消失,它在 WeakSet里面的引用就会自动消失。

    由于上面这个特点,WeakSet的成员是不适合引用的,因为它随时都可能消失。另外,由于 WeakSet内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数都不一样了,而垃圾回收机制何时运行又是不可预测的,因此 ES6规定 WeakSet不可遍历,这些特点同样适用于后面的 WeakMap结构。

// WeakSet本身也是一个构造函数,用来生成 WeakSet数据结构实例。WeakSet数据结构新建实例时,不允许直接传值初始化
let testWeakSet = new WeakSet({"a": 1, "b": 2});    // Uncaught TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))

let testWeakSet = new WeakSet();
testWeakSet.add({"a": 1, "b": 2});
console.log(testWeakSet);                            // WeakSet {{…}}
let tempObj = {'ab': 11, 'bc': 22};
testWeakSet.add(tempObj);
testWeakSet.add(tempObj);
console.log(testWeakSet);                            // WeakSet {{…}, {…}}    WeakSet数据结构内的成员也不允许重复
testWeakSet.add(1);                                  // Uncaught TypeError: Invalid value used in weak set  WeakSet的成员只能是对象,不能是其他类型的值
WeakSet实例只有三个原型方法:add(value)、delete(value)、has(value),功能和 Set数据结构一样;同时 WeakSet没有size属性,故而无法遍历它的成员,也就没有keys()、values()、entries()、forEach()等遍历操作。

WeakSet成员不能遍历,是因为成员都是弱引用,随时都可能消失,遍历机制无法保证成员的存在,很可能刚遍历结束,成员就不存在了。WeakSet的一个用处,是储存 DOM节点,而不用担心这些节点从文档内移除后,引发内存泄漏。

let testWeakSet = new WeakSet();
testWeakSet.add({"a": 1, "b": 2});
testWeakSet.add({'ab': 11, 'bc': 22});

console.log(testWeakSet.size);                             // undefined
console.log(testWeakSet.forEach);                          // undefined
testWeakSet.forEach(function (value, key) {                // Uncaught TypeError: testWeakSet.forEach is not a function
    console.log(key + ':' + value);
});

总结:在实际开发过程中 Set数据结构用的比较多,WeakSet用的并不多,甚至很少出现,但它对传入值必须是对象作了很好的判断,所以灵活应用还是有一定的用处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值