[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,

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

2.2 %MapIteratorPrototype%

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

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

  1. Let O be the this value.
  2. If Type(O) is not Object, throw a TypeError exception.
  3. If O does not have all of the internal slots of a Map Iterator Instance (23.1.5.3), throw a TypeError exception.
  4. Let m be O.[[Map]].
  5. Let index be O.[[MapNextIndex]].
  6. Let itemKind be O.[[MapIterationKind]].
  7. If m is undefined, return CreateIterResultObject(undefined, true).
  8. Assert: m has a [[MapData]] internal slot.
  9. Let entries be the List that is m.[[MapData]].
  10. Let numEntries be the number of elements of entries.
  11. NOTE: numEntries must be redetermined each time this method is evaluated.
  12. Repeat, while index is less than 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是有顺序的。

2.3 [[MapData]]

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

Map ( [ iterable ] )

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

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

2.4 Map.prototype.set

再看Map.prototype.set,

  1. Let M be the this value.
  2. If Type(M) is not Object, throw a TypeError exception.
  3. If M does not have a [[MapData]] internal slot, throw a TypeError exception.
  4. Let entries be the List that is M.[[MapData]].
  5. 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.
  6. If key is -0, set key to +0.
  7. Let p be the Record { [[Key]]: key, [[Value]]: value }.
  8. Append p as the last element of entries.
  9. Return M.

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


参考

mdn: Map

你可能感兴趣的:([JS] Map中keys的有序性)