第 19 期 - JavaScript ES6 中的 Symbol、Set、Map 及 WeakSet、WeakMap
logoFRONTALK AI/11月11日 16:31/阅读原文

摘要

本文主要介绍了 JavaScript ES6 中的 Symbol 类型、Set 和 Map 数据结构以及 WeakSet 和 WeakMap 的弱引用机制,还对 ES6 的其他特性进行了总结。

一、Symbol 类型

1.1 Symbol 基础使用

Symbol 是 ES6 新增的基本数据类型,用于创建独一无二的标识符,主要用作对象属性的键以防止属性名冲突。在 ES6 之前,对象属性名都是字符串形式,容易造成冲突。例如在开发中使用混入或展开语法结合时,同名属性会被覆盖。

const obj1 = { foo: 'bar', x: 42 };
const obj2 = { foo: 'baz', y: 13 };
const mergedObj = {...obj1,...obj2 };//foo 同名,后者覆盖前者

Symbol 值通过 Symbol 函数生成,每次生成的值都是独一无二的,即使传入相同的描述。例如:

let sym1 = Symbol('description');
let sym2 = Symbol('description');
console.log(sym1 === sym2); // 输出:false

从内存分配角度看,每个 Symbol 值指向独立内存地址。虽然 Symbol 的标识无法访问,但在 ES10 加入的 description 实例属性可以获取描述。

let sym1 = Symbol('小余');
console.log(sym1.description);//小余

1.2 Symbol 值作为 key

在 ES6 之前,对象的 key 只有字符串形式,ES6 之后增加了 Symbol。使用 Symbol 作为 key 时要用动态属性名写法[]。

const s1 = Symbol()
const s2 = Symbol()
const obj = {
  name:"xiaoyu",
  age:20,
  [s1]:"aaa",
  [s2]:"bbb"
}

在原有对象上新增内容与 Symbol 结合也需要使用方括号。使用.操作符访问属性适合属性名已知的情况,而[]允许动态确定属性名。使用 Symbol 作为 key 的属性名在遍历/Object.keys 等情况中获取不到,需要用 getOwnPropertySymbols 方法获取。

console.log(Object.keys(obj));//获取不到['name', 'age']
console.log(Object.getOwnPropertyNames(obj));//[ 'name', 'age' ]
console.log(Object.getOwnPropertySymbols(obj));//[Symbol(), Symbol()]

1.3 Symbol 方法

Symbol 的方法分为实例方法和静态方法。实例方法有 toString、valueOf 与Symbol.toPrimitive。这里主要讲静态方法 Symbol.for 与 Symbol.keyFor。这两个方法以 Symbol 注册表为根基,for 方法是查询 Symbol 本身,keyFor 方法查询的是 Symbol 的 description 内容。

var globalSym = Symbol.for("foo");
Symbol.keyFor(globalSym); // "foo"
var localSym = Symbol();
Symbol.keyFor(localSym); // undefined

二、数据结构 Set

2.1 Set 的基本使用

ES6 之前存储数据的结构主要是数组和对象,ES6 新增了 Set、Map 以及 WeakSet、WeakMap。Set 可以保存数据,类似于数组但元素不能重复。Set 可用于给数组去重。

const numbers = [2, 3, 4, 4, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 5, 32, 3, 4, 5];
const numberSet = new Set(numbers);
const uniqueNumbers1 = Array.from(numberSet);
console.log(uniqueNumbers1); // [2, 3, 4, 5, 6, 7, 32]

Set 中的元素是通过内存地址判别是否重复的,对于对象类型,如果是不同的内存地址就不会去重。

2.2 Set 的常见方法

Set 只有一个实例属性 Size,表示集合中元素的个数。常见方法有 add(增)、delete(删)、has(查)、clear(清除所有)和 forEach(遍历)。

const numbers = [2, 3, 4, 4, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 5, 32, 3, 4, 5];
const set = new Set(numbers)
//增
set.add("666")
//删
set.delete(2)
//判断存在
console.log(set.has("666"));
//forEach 遍历
set.forEach(item => console.log(item))
//清空
set.clear()

三、WeakSet 的使用

3.1 WeakSet 常见方法

WeakSet 与 Set 类似,但有区别。区别一是 WeakSet 只能存放对象类型;区别二是 WeakSet 对元素的引用是弱引用,对象的存在不影响其生命周期。WeakSet 只有 add、delete、has 三个实例方法。

const ws = new WeakSet();
const foo = {};
const bar = {};
ws.add(foo);
ws.add(bar);
ws.has(foo); // true
ws.delete(foo);
ws.has(foo); // false

3.2 弱引用与强引用

强引用下,只要引用关系存在,垃圾回收器就不会回收被引用的对象,如变量引用、属性引用、数组元素、函数闭包等都是强引用。而弱引用的指向会被垃圾回收所忽略。

3.3 WeakSet 的应用

WeakSet 不能遍历,因为遍历可能导致对象不能正常销毁,且会违背弱引用的设计初衷。WeakSet 的一个应用场景是在类方法调用中,可用于判定 this 是否被篡改。

四、数据结构 Map

4.1 Map 的基本使用

Map 用于存储映射关系(键值对),与对象存储映射关系不同,Map 允许使用对象类型作为 key。

const map = new Map()
const obj1= {name:"小余"}
const obj2= {name:"coderwhy"}
map.set(obj1,'aaa')
map.set(obj2,'bbb')
console.log(map);

Map 可以设置初始值,其初始化格式为每个内层数组必须恰好有两个元素,第一个元素作为键,第二个元素作为值。

4.2 Map 的常见方法

Map 的实例方法有 set、get、has、delete、clear、forEach 等,还有实例属性 size。

const info = {name:"小余"}
const info2 = {name:"why"}
const map = new Map()
map.set(info,"XiaoYu")
map.set(info2,"coderwhy")
console.log(map.size);
console.log(map.get(info));
map.forEach((item,key) => {
    console.log(item,key,"forEach 水印");
});

五、WeakMap 的使用

5.1 WeakMap 常见方法

WeakMap 与 Map 对应,区别一是 WeakMap 的 key 只能使用对象;区别二是 WeakMap 的 key 对对象的引用是弱引用。WeakMap 常见方法有 set、get、has、delete。

const weakMap = new WeakMap();
const objKey = { name: 'coderwhy' };
weakMap.set(objKey, '了解真相 才会获得真正的自由');
console.log(weakMap.get(objKey));

5.2 WeakMap 的应用

WeakMap 的键不可枚举,不能遍历。WeakMap 适合用于响应式系统中,如 Vue 3 中组件和响应式对象的依赖收集机制。

六、ES6 小总结

ES6 是一次大的版本更新,除了上述特性外,还有 Proxy、Reflect、Promise、ES Module 模块化开发等重要特性。后续还会学习 ES7 - ES12 的内容。

 

扩展阅读

Made by 捣鼓键盘的小麦 / © 2025 Front Talk 版权所有