每天一点面试题(十二) --------------map补充 没什么用 看了记下来

1.[JS] Map中keys的有序性

1. 背景

mdn: Map是ES6中的内置全局对象,其中保存了多个键值对映射关系。
它的key 和value 都可以是js中的任意对象。例如,

const m = new Map;

const objectKey1 = {};
const objectKey2 = {};
const stringKey3 = '';
const numberKey4 = Infinity;

m.set(objectKey1, 1);
m.set(objectKey2, 2);
m.set(stringKey3, 3);
m.set(numberKey4, 4);

const iter = m.keys();
console.assert(iter.next().value === objectKey1);
console.assert(iter.next().value === objectKey2);
console.assert(iter.next().value === stringKey3);
console.assert(iter.next().value === numberKey4);

这里我们看到,m.keys()是按key的插入顺序返回的。
mdn中的解释如下,

Map.prototype.keys()
Returns a new Iterator object that contains the keys for each element in the Map object in insertion order.
返回一个新的迭代器对象,该对象按插入顺序包含映射对象中每个元素的键

但是这种顺序性到底是否与实现相关呢,还是一种规范呢。
下面我们查阅ECMAScript规范来确认下。

2. Map

规范中Map相关的章节中,并没有提及顺序性。
只是对实现有这样的限制,

Map object must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection.
映射对象必须使用哈希表或其他机制来实现,这些机制平均提供的访问时间是集合中元素数量的次线性。
2.1 Map.prototype.keys

我们直接看Map.prototype.keys的操作语义,

    1.Let M be the this value.
    2.Return ? CreateMapIterator(M, "key").

其中,CreateMapIterator会返回一个iterator,

If Type(map) is not Object, throw a TypeError exception.
If map does not have a [[MapData]] internal slot, throw a TypeError exception.
Let iterator be ObjectCreate(%MapIteratorPrototype%, « [[Map]], [[MapNextIndex]], [[MapIterationKind]] »).
Set iterator.[[Map]] to map.
Set iterator.[[MapNextIndex]] to 0.
Set iterator.[[MapIterationKind]] to kind.
Return iterator.

如果Type(map)不是Object,则抛出一个TypeError异常。
如果map没有[[MapData]]内部槽,则抛出一个类型错误异常。
让迭代器是ObjectCreate(%MapIteratorPrototype%,«[[Map]], [[MapNextIndex]], [[MapIterationKind]]])。
设置迭代器。要映射[[图]]。
设置迭代器。[[MapNextIndex]]为0。
设置迭代器。[[MapIterationKind]]。
返回迭代器。
2.2 %MapIteratorPrototype%

%MapIteratorPrototype%是Map返回的所有iterator的原型对象。

我们来看它的next方法是怎样定义的,

Let O be the this value.
If Type(O) is not Object, throw a TypeError exception.
If O does not have all of the internal slots of a Map Iterator Instance (23.1.5.3), throw a TypeError exception.
Let m be O.[[Map]].
Let index be O.[[MapNextIndex]].
Let itemKind be O.[[MapIterationKind]].
If m is undefined, return CreateIterResultObject(undefined, true).
Assert: m has a [[MapData]] internal slot.
Let entries be the List that is m.[[MapData]].
Let numEntries be the number of elements of entries.
NOTE: numEntries must be redetermined each time this method is evaluated.
Repeat, while index is less than numEntries,

设O是这个值。
如果Type(O)不是Object,则抛出一个TypeError异常。
如果O没有Map迭代器实例的所有内部插槽(23.1.5.3),则抛出一个TypeError异常。	
让我是O.[[地图]]。	
设index为o [[MapNextIndex]]。	
设itemKind为o [[MapIterationKind]]。	
如果m未定义,则返回CreateIterResultObject(未定义,为真)。	
断言:m有一个[[MapData]]内部槽。	
让条目为m.[[MapData]]的列表。	
设numEntries是项的元素数目。	
注意:每次计算此方法时,必须重新确定numEntries。	
重复,当索引小于numEntries时,

其中第9步是关键,entries是一个List,它的值是m.[[MapData]]。
其中List是一个规范内置类型(Specification Types)。

The List type is used to explain the evaluation of argument lists in new expressions, in function calls, and in other algorithms where a simple ordered list of values is needed. Values of the List type are simply ordered sequences of list elements containing the individual values.
List类型用于解释新表达式、函数调用和其他需要简单有序值列表的算法中参数列表的计算。列表类型的值只是包含各个值的列表元素的有序序列。

因此,List是有顺序的。

2.3 [[MapData]]

那么m.[[MapData]]是怎么来的呢?它是在Map的构造函数中初始化的。

Map ( [ iterable ] )

If NewTarget is undefined, throw a TypeError exception.
Let map be ? OrdinaryCreateFromConstructor(NewTarget, "%MapPrototype%", « [[MapData]] »).
Set map.[[MapData]] to a new empty List.
If iterable is not present, or is either undefined or null, return map.
Let adder be ? Get(map, "set").
Return ? AddEntriesFromIterable(map, iterable, adder).

第3步,构造函数初始化map.[[MapData]]为一个空List。

2.4 Map.prototype.set

再看Map.prototype.set,

Let M be the this value.
If Type(M) is not Object, throw a TypeError exception.
If M does not have a [[MapData]] internal slot, throw a TypeError exception.
Let entries be the List that is M.[[MapData]].
For each Record { [[Key]], [[Value]] } p that is an element of entries, do
5.1 If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, then
a. Set p.[[Value]] to value.
b. Return M.
If key is -0, set key to +0.
Let p be the Record { [[Key]]: key, [[Value]]: value }.
Append p as the last element of entries.
Return M.

设M是这个值。
如果Type(M)不是Object,则抛出一个TypeError异常。
如果M没有[[MapData]]内部槽,则抛出一个类型错误异常。
让条目为M.[[MapData]]的列表。
对于每个记录{[[Key]], [[Value]]} p是一个条目元素,do
5.1如果p。[[键]]不是空的,SameValueZero(p。那么,[[键]],键)为真
一套。p。价值价值[[]]。
b。M。
如果键值为-0,则将键值设置为+0。
设p为记录{[[Key]]: Key, [[Value]]: Value}。
将p作为项的最后一个元素。
返回M。

看第8步,可见Map中的key在逻辑上是顺序存储的。

发布了38 篇原创文章 · 获赞 1 · 访问量 547

猜你喜欢

转载自blog.csdn.net/weixin_43718291/article/details/103467776