如何在本地存储(或其他地方)中保留ES6地图?

Let*_*ion 61 javascript json dictionary ecmascript-6

var a = new Map([[ 'a', 1 ]]);
a.get('a') // 1

var forStorageSomewhere = JSON.stringify(a);
// Store, in my case, in localStorage.

// Later:
var a = JSON.parse(forStorageSomewhere);
a.get('a') // TypeError: undefined is not a function
Run Code Online (Sandbox Code Playgroud)

不幸的是JSON.stringify(a);简单地返回'{}',这意味着当恢复时它变成一个空对象.

我发现es6-mapify允许在Map和普通对象之间进行向上/向下转换,因此这可能是一个解决方案,但我希望我只需依靠外部依赖来持久保存我的地图.

Ber*_*rgi 73

假设您的密钥和值都是可序列化的,

localStorage.myMap = JSON.stringify(Array.from(map.entries()));
Run Code Online (Sandbox Code Playgroud)

应该管用.反之,请使用

map = new Map(JSON.parse(localStorage.myMap));
Run Code Online (Sandbox Code Playgroud)

  • 刚刚在 Chrome 中尝试过这个,不需要`.entries()`;这似乎做同样的事情:`localStorage.myMap = JSON.stringify(Array.from(map));` (3认同)
  • @bmaupin 是的,`Array.from` 使用的默认 `[Symbol.iterator]` 与 Map 上的 `.entries` 相同,但我想将其拼写出来,以便很明显我们在这里使用迭代器。 (2认同)

Ori*_*iol 10

通常,序列化仅在此属性成立时才有用

deserialize(serialize(data)).get(key) ? data.get(key)
Run Code Online (Sandbox Code Playgroud)

哪里a ? b可以定义为serialize(a) === serialize(b).

将对象序列化为JSON时,这是满足的:

var obj1 = {foo: [1,2]},
    obj2 = JSON.parse(JSON.stringify(obj1));
obj1.foo; // [1,2]
obj2.foo; // [1,2] :)
JSON.stringify(obj1.foo) === JSON.stringify(obj2.foo); // true :)
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为属性只能是字符串,可以无损地序列化为字符串.

但是,ES6映射允许任意值作为键.这是有问题的,因为对象由它们的引用唯一标识,而不是它们的数据.在序列化对象时,会丢失引用.

var key = {},
    map1 = new Map([ [1,2], [key,3] ]),
    map2 = new Map(JSON.parse(JSON.stringify([...map1.entries()])));
map1.get(1); // 2
map2.get(1); // 2 :)
map1.get(key); // 3
map2.get(key); // undefined :(
Run Code Online (Sandbox Code Playgroud)

所以我想说一般来说不可能以有用的方式做到这一点.

对于那些可以工作的情况,很可能你可以使用普通对象而不是地图.这也有以下优点:

  • 它可以在不丢失密钥信息的情况下被字符串化为JSON.
  • 它适用于旧版浏览器.
  • 它可能会更快.


Jon*_*ych 5

建立客奥里奥尔的 答案,我们可以做的更好一点.只要有原始根或进入地图,我们仍然可以使用对象的对象引用,并且可以从该根密钥中传递地找到每个对象密钥.

修改Oriol的例子以使用Douglas Crockford的JSON.decycle和JSON.retrocycle我们可以创建一个处理这种情况的地图:

var key = {},
    map1 = new Map([ [1, key], [key, 3] ]),
    map2 = new Map(JSON.parse(JSON.stringify([...map1.entries()]))),
    map3 = new Map(JSON.retrocycle(JSON.parse(JSON.stringify(JSON.decycle([...map1.entries()])))));
map1.get(1); // key
map2.get(1); // key
map3.get(1); // key
map1.get(map1.get(1)); // 3 :)
map2.get(map2.get(1)); // undefined :(
map3.get(map3.get(1)); // 3 :)
Run Code Online (Sandbox Code Playgroud)

循环和后循环可以编码JSON中的循环结构和凹陷.如果我们想要在对象之间建立关系而不在这些对象本身上创建其他属性,或者希望通过使用ES6 Map可以互换地将基元与对象相关联(反之亦然),这将非常有用.

一个缺陷是我们不能将原始密钥对象用于新映射(map3.get(key);将返回undefined).但是,持有原始密钥引用,但新解析的JSON映射似乎是一个非常不可能的情况.


Ode*_*ner 5

吹口哨:

JSON.stringify([...myMap])
Run Code Online (Sandbox Code Playgroud)

  • 这真是太好了。我希望我可以不止一次投票。对于那些阅读者,您可以通过以下简单的方法对其进行反序列化:`let deserialized = new Map(JSON.parse(serialisedMap));` (2认同)

Has*_*own 5

如果你为你拥有的toJSON()任何对象实现你自己的函数,那么普通的旧函数就可以工作了!classJSON.stringify()

Maps 与Arrays 代表钥匙?Maps 与其他Map值?一个Map里面有一个常规的Object?甚至可能是您自己的定制课程;简单的。

Map.prototype.toJSON = function() {
    return Array.from(this.entries());
};
Run Code Online (Sandbox Code Playgroud)

就是这样! 这里需要原型操作。您可以手动添加toJSON()所有非标准内容,但实际上您只是在回避 JS 的强大功能

演示版

test = {
    regular : 'object',
    map     : new Map([
        [['array', 'key'], 7],
        ['stringKey'     , new Map([
            ['innerMap'    , 'supported'],
            ['anotherValue', 8]
        ])]
    ])
};
console.log(JSON.stringify(test));
Run Code Online (Sandbox Code Playgroud)

输出:

{"regular":"object","map":[[["array","key"],7],["stringKey",[["innerMap","supported"],["anotherValue",8]]]]}
Run Code Online (Sandbox Code Playgroud)

不过,反序列化一直回到 real Maps 并不是自动的。使用上面的结果字符串,我将重新制作地图以提取一个值:

test2 = JSON.parse(JSON.stringify(test));
console.log((new Map((new Map(test2.map)).get('stringKey'))).get('innerMap'));
Run Code Online (Sandbox Code Playgroud)

输出

"supported"
Run Code Online (Sandbox Code Playgroud)

这有点混乱,但是用一点魔法, 你也可以使反序列化变得自动

Map.prototype.toJSON = function() {
    return ['window.Map', Array.from(this.entries())];
};
Map.fromJSON = function(key, value) {
    return (value instanceof Array && value[0] == 'window.Map') ?
        new Map(value[1]) :
        value
    ;
};
Run Code Online (Sandbox Code Playgroud)

现在 JSON 是

{"regular":"object","test":["window.Map",[[["array","key"],7],["stringKey",["window.Map",[["innerMap","supported"],["anotherValue",8]]]]]]}
Run Code Online (Sandbox Code Playgroud)

反序列化和使用非常简单Map.fromJSON

test2 = JSON.parse(JSON.stringify(test), Map.fromJSON);
console.log(test2.map.get('stringKey').get('innerMap'));
Run Code Online (Sandbox Code Playgroud)

输出(并且没有new Map()使用)

"supported"
Run Code Online (Sandbox Code Playgroud)

演示版