ES6 映射是如何在底层实现的?
Posted
技术标签:
【中文标题】ES6 映射是如何在底层实现的?【英文标题】:How are ES6 maps implemented under the hood? 【发布时间】:2020-02-20 22:06:30 【问题描述】:在一次采访中,我被要求实现键/值对的数据结构,其中键可以是一个对象,我知道这可以使用 ES6 映射,但是它们如何在 javascript 中的引擎盖下工作,其中键被严格字符串化并且还实现了与哈希表/对象相同的恒定查找时间?
谢谢。
【问题讨论】:
我猜 Maps 使用对象的引用作为哈希键。 是的,这是有道理的,但是如果更改该引用,那么已经生成的哈希索引将无效? 如何更改对象的引用?如果一个对象有不同的引用,它就是一个不同的对象。 是的,但是Map
和get
/set
中使用的键与引用对象属性的键不同。而且这些映射键没有被字符串化。
@t.niese 确实如此。这有点术语冲突——在 JS 中,通常“key”指的是对象的属性。一般来说,在谈论地图时,“键”只是用于查找关联值的项目。这变得很混乱,因为普通的 JS 对象 do 以相同的方式使用键,因此键在 JS 中成为“属性名称”的同义词,这是 正确的,因为它们以相同的方式工作用于在良好但不正确的情况下获取值,因为地图通常不会通过其属性在地图中获取值。因此,存在抽象泄漏。
【参考方案1】:
我在一次采访中被问到这个问题,我无法保证 Map 是如何在后台实际实现的(最终在这里寻找自己)。然而,这是我与面试官一起制定的方法(注意,要求仅针对 DOM 节点键,但我确实认为对象是最难处理的键,其余的可以通过额外的代码轻松处理),我认为它至少是有见地的:
class Map
constructor()
this.map = ; // internal key/value object
this.trackerKey = Symbol();
set(key, value)
let lookupKey = key[this.trackerKey];
if (!lookupKey)
lookupKey = Symbol();
key[this.trackerKey] = lookupKey;
this.map[lookupKey] = value;
has(key)
return key.hasOwnProperty(this.trackerKey);
get(key)
const lookupKey = key[this.trackerKey];
return this.map[lookupKey];
delete(key)
const lookupKey = key[this.trackerKey];
delete key[this.trackerKey];
delete this.map[lookupKey];
基本上,这个想法是在底层使用符号来跟踪对象。 trackerKey
是一个(符号)属性,我们添加到所有传入的键中。由于它是在实例内部定义的符号,因此其他任何东西都无法引用它。
所以当我们转到set
时,我们检查对象上是否存在trackerKey 属性。如果不是,我们将其设置为新符号。然后,我们将内部映射中该键的值设置为传入的值。
has
和 get
现在是相当简单的查找。我们检查我们的跟踪器密钥是否存在于密钥上,以查看它是否包含在我们的 Map 中。对于get
,我们可以简单地从它的 trackerKey 属性中获取对象的内部查找键。
对于delete
,我们只需要从key对象中删除trackerKey属性,然后在我们内部的map对象中删除该属性即可。
有趣而有见地的练习!希望这会有所帮助:)
【讨论】:
"没有其他东西可以引用它" --trackerKey
可以通过Object.getOwnPropertySymbols
或Reflect.ownKeys
从外部检索,但你是对的因为没有代码通常会这样做。
@FZs 很好的标注。这是一个很好的观点,可以使用这些方法检索符号键。但我们至少保证外部代码访问trackerKey
的唯一方法是通过您提到的反射,否则我们保证trackerKey
中的唯一性,这样外部代码就不会意外 i> 覆盖或与之碰撞。
注意,如果对象已被密封或冻结,这也不起作用。不确定我们如何处理该用例【参考方案2】:
Map
的键不是Map
对象的键。它们是 .get
和 .set
方法的参数。
const map = new Map;
const key = ;
map.set(key, "stuff"); // key is passed as an argument, not stringified
map[key] // key would get stringified here according to language semantics, but even then it would be undefined as maps keys aren't keys of the map object
这些方法在后台所做的是具体实现。
【讨论】:
确实是的,我意识到,我的问题有点含糊,是否有任何资源可以挖掘以了解 .get/.set 的工作原理? 更准确的说法是 Map 的键不是 Map 对象的 properties。它们仍然可以称为“钥匙”。 您可以深入研究规范以找到这些方法必须满足的规则,或其中一种实现的源代码以上是关于ES6 映射是如何在底层实现的?的主要内容,如果未能解决你的问题,请参考以下文章