z12.引用数据类型
问题1:Map、WeakMap、Set和WeakSet
Map
对比 Map 和 Object 的键名:
Object 对象:是一堆键 / 值对的集合。但是,JavaScript 给对象的键名有限制;只可以使用
number
、string
、symbol
作为键名。Map 映射 / 字典:是一堆键 / 值对的集合。与对象不同的是,Map 的键名可以是任何 JavaScript 数据类型。
所以,作为承载数据的容器,Map 是 Object 功能上的扩充。
从性能上来说:
- Object 的查找速度更快,Map 比 Object 多 50% 多内存占用。
- Map 插入、删除数据的操作比 Object 更优;
// 创建
var m = new Map();
var x = {id: 1},
y = {id: 2};
// 插入
m.set(x, "foo");
m.set(y, "bar"); // Map(2) {{…} => "foo", {…} => "bar"}
// 读取
m.get(x); // foo
m.get(y); // bar
// 删除
m.delete(y) // true,「某个项目」
m.clear() // 「删除全部项目」
// 长度属性
m.size // 1,「注意没有括号!⚠️」
// 是可迭代对象
m // Map(2) {{…} => "foo", {…} => "bar"}
// 获取值
console.log([...m.values()]) // (2) ["foo", "bar"]
// 获取键
console.log([...m.keys()])
// (2) [{…}, {…}]
// 0: {id: 1}
// 1: {id: 2}
// 获取键/值
console.log([...m.entries()])
// (2) [Array(2), Array(2)]
// 0: (2) [{…}, "foo"]
// 1: (2) [{…}, "bar"]
WeakMap
WeakMap 是 Map 的衍生品,WeakMap 键只能是对象。同时这些对象是被弱持有的,如果对象本身被垃圾回收的话,在 WeakMap 中这个项目也会被移除。
对比,WeakMap 和 Map 的内存分配与垃圾回收:
Map:如果 Map 中的键是一个对象,如果这个对象所有的引用都被解除,也不会执行 GC 回收,Map 中依然持有其对应的项目。
WeakMap:如果 WeakMap 中的键 只能 是对象,如果这个对象所有的引用都被解除,就会执行 GC 回收,WeakMap 中其对应的项目也会因此被删除。
WeakMap 不是一个可迭代对象,无法生成一个迭代器;
weakMap 没有 size
属性和 clear()
方法。
let m = new WeakMap()
Set
Set 集合,是一个值的集合,且其中的值唯一(不允许重复)。
- Set 没有
set()
、没有get()
方法。- Set 是通过
add()
方法添加项目的; - Set 没有键名,无法通过
get()
获得值,而是用has()
判断值是否存在。
- Set 是通过
// 创建
var s = new Set()
var x = {id: 1},
y = {id: 2};
// 插入
s.add(x);
s.add(y);
s.add(x); // 不支持相同内容重复添加
// 长度属性
s.size // 2,「注意是属性,没有括号」
// 删除
s.delete(y) // 删 某个项目
s.clear() // 删 全部项目
// 是可迭代对象
// 获取值,这两个方法返回值相同
console.log([...s.values()])
console.log([...s.keys()])
// (2) [{…}, {…}]
// 0: {id: 1}
// 1: {id: 2}
// 获取键/值,会返回 [value, value]:
console.log([...s.entries()])
// (2) [Array(2), Array(2)]
// 0: Array(2)
// 0: {id: 1}
// 1: {id: 2}
// 1: Array(2)
// 0: {id: 1}
// 1: {id: 2}
WeakSet
WeakSet 是 Set 的衍生品,WeakSet 值是弱持有的,如果其值是一个对象的话,当对象本身即将被垃圾回收,在 WeakSet 中这个项目也会被移除。
对比,WeakSet 和 Set 的内存分配与垃圾回收:
Set:如果 Set 中的值是一个对象,如果这个对象所有的引用都被解除,也不会执行 GC 回收,Set 中依然持有其对应的项目。
WeakSet:如果 WeakSet 中的值是一个对象,如果这个对象所有的引用都被解除,就会执行 GC 回收,WeakSet 中其对应的项目也会因此被删除。
WeakSet 不是一个可迭代对象,无法生成一个迭代器;
WeakSet 没有 size
属性和 clear()
方法。
let s = new WeakSet()
问题2:Symbol
- symbol 没有字面量形式,symbol 是一个内部值,是无法在代码中获得的、唯一的值。
- 不能对
Symbol()
使用new
。 Symbol(..)
的参数是可选的,如果传入这个参数,就会为这个Symbol
添加一个对用户友好的描述字符串。- 判断一个 symbol:
typeof
关键字。
Symbol.for()
全局符号注册 global symbol registry:Symbol.for()
会在 全局 symbol 注册表 中搜索,查看是否有描述文字相同的符号已经存在:
- 如果存在,就返回这个 symbol;
- 如果不存在,就在全局符号注册表中创建一个新的 symbol,然后返回它。
// 全局注册 symbol
const MY_SYMBOL = Symbol.for("my symbol")
console.log(MY_SYMBOL) // Symbol(my symbol)
默认不可遍历
对象的属性名如果是 Symbol,则无法用 Object.getOwnPropertyNames(obj)
遍历出来,而是需要用 Object.getOwnPropertySymbols(obj)
。
let obj = {
foo: 42,
1: 2,
[MY_SYMBOL]: 123
}
Object.getOwnPropertyNames(obj); // (2) ["1", "foo"]
Object.getOwnPropertySymbols(obj); // [Symbol(my symbol)]
内置 Symbol
ES6 有许多内置好的 Symbol,它们可以暴露 JavaScript 对象值的各种元特性。
这些 Symbol 并不是注册在全局 Symbol 表中,而是作为 Symbol 函数的属性保存,常用 @@
来表示,如 [Symbol.iterator]
:@@iterator
。
[Symbol.iterator]
:作为 “可迭代对象” 的标志,for...of
在遍历对象时会调用这个函数。