localstorage (또는 다른 곳)에서 ES6 맵을 유지하려면 어떻게해야합니까?
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
불행히도 JSON.stringify(a);
단순히 '{}'를 반환합니다. 이는 a가 복원 될 때 빈 개체가됨을 의미합니다.
나는 지도와 일반 객체 사이의 업 / 다운 캐스팅을 허용하는 es6-mapify 를 찾았 으므로 하나의 해결책이 될 수 있지만 단순히 내지도를 유지하기 위해 외부 종속성에 의존해야 할 필요가 있기를 바랐습니다.
키와 값이 모두 직렬화 가능하다고 가정하고,
localStorage.myMap = JSON.stringify(Array.from(map.entries()));
작동해야합니다. 반대의 경우
map = new Map(JSON.parse(localStorage.myMap));
일반적으로 직렬화는이 속성이
deserialize(serialize(data)).get(key) ≈ data.get(key)
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 :)
속성은 문자열로만 무손실 직렬화 될 수 있기 때문에 작동합니다.
그러나 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 :(
그래서 나는 일반적으로 유용한 방법 으로 그것을 할 수 없다고 말하고 싶습니다 .
그리고 그것이 작동하는 경우에는 아마도 map 대신 일반 객체를 사용할 수 있습니다 . 또한 다음과 같은 이점이 있습니다.
- 주요 정보를 잃지 않고 JSON으로 문자열화할 수 있습니다.
- 이전 브라우저에서 작동합니다.
- 더 빠를 수 있습니다.
Oriol의 답변을 바탕 으로 조금 더 잘할 수 있습니다. 기본 루트 또는 맵 입구가 있고 각 오브젝트 키가 해당 루트 키에서 전 이적으로 발견 될 수있는 한 키에 대한 오브젝트 참조를 계속 사용할 수 있습니다.
Douglas Crockford의 JSON.decycle 및 JSON.retrocycle 을 사용하도록 Oriol의 예제를 수정 하면이 경우를 처리하는 맵을 만들 수 있습니다.
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 :)
Decycle 및 Retrocycle을 사용하면 주기적 구조와 dag를 JSON으로 인코딩 할 수 있습니다. 이는 객체 자체에 추가 속성을 생성하지 않고 객체 간의 관계를 구축하거나 ES6 맵을 사용하여 기본 요소를 객체에 상호 교환 적으로 연관시키려는 경우 유용합니다.
한 가지 함정은 새 맵에 원래 키 객체를 사용할 수 없다는map3.get(key);
것입니다 ( undefined 반환). 그러나 원래 키 참조를 보유하고 있지만 새로 구문 분석 된 JSON 맵은 가능성이 거의없는 케이스처럼 보입니다.
호루라기로 청소 :
JSON.stringify([...myMap])
다차원지도가있는 경우 수락 된 답변이 실패합니다. Map 객체는 다른 Map 객체를 키 또는 값으로 사용할 수 있다는 점을 항상 염두에 두어야합니다.
따라서이 작업을 처리하는 더 좋고 안전한 방법은 다음과 같습니다.
function arrayifyMap(m){
return m.constructor === Map ? [...m].map(([v,k]) => [arrayifyMap(v),arrayifyMap(k)])
: m;
}
이 도구가 있으면 언제든지 다음과 같이 할 수 있습니다.
localStorage.myMap = JSON.stringify(arrayifyMap(myMap))
당신이 가지고 toJSON()
있는 어떤 class
객체에 대해 당신 자신의 함수를 구현한다면, 보통의 오래된 JSON.stringify()
것만이 작동 할 것입니다!
Map
s with Array
s for keys? Map
다른 Map
값으로? Map
정기적으로 내부 Object
? 어쩌면 당신 만의 커스텀 클래스 일 수도 있습니다. 쉬운.
Map.prototype.toJSON = function() {
return Array.from(this.entries());
};
그게 다야! 여기서 프로토 타입 조작이 필요합니다. toJSON()
모든 비표준 항목에 수동으로 추가 할 수 있지만 실제로는 JS의 힘을 피하는 것입니다.
test = {
regular : 'object',
map : new Map([
[['array', 'key'], 7],
['stringKey' , new Map([
['innerMap' , 'supported'],
['anotherValue', 8]
])]
])
};
console.log(JSON.stringify(test));
출력 :
{"regular":"object","map":[[["array","key"],7],["stringKey",[["innerMap","supported"],["anotherValue",8]]]]}
Map
하지만 실제로 돌아가는 역 직렬화 는 자동으로 수행되지 않습니다. 위의 결과 문자열을 사용하여 맵을 다시 만들어 값을 추출합니다.
test2 = JSON.parse(JSON.stringify(test));
console.log((new Map((new Map(test2.map)).get('stringKey'))).get('innerMap'));
출력
"supported"
그것은 약간 지저분하지만 약간의 마법 소스 를 사용하면 deserialization automagic도 만들 수 있습니다 .
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
;
};
이제 JSON은
{"regular":"object","test":["window.Map",[[["array","key"],7],["stringKey",["window.Map",[["innerMap","supported"],["anotherValue",8]]]]]]}
그리고 역 직렬화 및 사용은 Map.fromJSON
test2 = JSON.parse(JSON.stringify(test), Map.fromJSON);
console.log(test2.map.get('stringKey').get('innerMap'));
출력 ( new Map()
사용 되지 않음 )
"supported"
빠진 한 가지는 Map이 ORDERED 구조 라는 것입니다. 즉, 입력 된 첫 번째 항목이 반복 될 때 첫 번째 항목이 나열됩니다.
이다 NOT 자바 스크립트 객체처럼. 이 유형의 구조가 필요했기 때문에 (Map을 사용했습니다) JSON.stringify가 작동하지 않는다는 것을 알아 내려면 고통 스럽지만 이해할 수 있습니다.
결국 가장 기본적인 '유형'에 대해서만 JSON.stringify를 사용하여 모든 구문 분석을 의미하는 'value_to_json'함수를 만들었습니다.
불행히도 .toJSON ()으로 MAP를 서브 클래 싱하는 것은 JSON_string이 아닌 값을 제외하고는 작동하지 않습니다. 또한 유산으로 간주됩니다.
내 사용 사례는 예외적입니다.
관련 :
- https://github.com/DavidBruant/Map-Set.prototype.toJSON/issues/16
- JSON은 Infinity와 NaN을 제외했습니다. ECMAScript의 JSON 상태?
- ES5 세트 및 맵을 포함하는 객체를 문자열 화하는 방법은 무엇입니까?
- JSON 문자열 화
function value_to_json(value) {
if (value === null) {
return 'null';
}
if (value === undefined) {
return 'null';
}
//DEAL WITH +/- INF at your leisure - null instead..
const type = typeof value;
//handle as much as possible taht have no side effects. function could
//return some MAP / SET -> TODO, but not likely
if (['string', 'boolean', 'number', 'function'].includes(type)) {
return JSON.stringify(value)
} else if (Object.prototype.toString.call(value) === '[object Object]') {
let parts = [];
for (let key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
parts.push(JSON.stringify(key) + ': ' + value_to_json(value[key]));
}
}
return '{' + parts.join(',') + '}';
}
else if (value instanceof Map) {
let parts_in_order = [];
value.forEach((entry, key) => {
if (typeof key === 'string') {
parts_in_order.push(JSON.stringify(key) + ':' + value_to_json(entry));
} else {
console.log('Non String KEYS in MAP not directly supported');
}
//FOR OTHER KEY TYPES ADD CUSTOM... 'Key' encoding...
});
return '{' + parts_in_order.join(',') + '}';
} else if (typeof value[Symbol.iterator] !== "undefined") {
//Other iterables like SET (also in ORDER)
let parts = [];
for (let entry of value) {
parts.push(value_to_json(entry))
}
return '[' + parts.join(',') + ']';
} else {
return JSON.stringify(value)
}
}
let m = new Map();
m.set('first', 'first_value');
m.set('second', 'second_value');
let m2 = new Map();
m2.set('nested', 'nested_value');
m.set('sub_map', m2);
let map_in_array = new Map();
map_in_array.set('key', 'value');
let set1 = new Set(["1", 2, 3.0, 4]);
m2.set('array_here', [map_in_array, "Hello", true, 0.1, null, undefined, Number.POSITIVE_INFINITY, {
"a": 4
}]);
m2.set('a set: ', set1);
const test = {
"hello": "ok",
"map": m
};
console.log(value_to_json(test));
// store
const mapObj = new Map([['a', 1]]);
localStorage.a = JSON.stringify(mapObj, replacer);
// retrieve
const newMapObj = JSON.parse(localStorage.a, reviver);
// required replacer and reviver functions
function replacer(key, value) {
const originalObject = this[key];
if(originalObject instanceof Map) {
return {
dataType: 'Map',
value: Array.from(originalObject.entries()), // or with spread: value: [...originalObject]
};
} else {
return value;
}
}
function reviver(key, value) {
if(typeof value === 'object' && value !== null) {
if (value.dataType === 'Map') {
return new Map(value.value);
}
}
return value;
}
여기에 대체자 및 리바이 버 기능에 대한 설명을 썼습니다 https://stackoverflow.com/a/56150320/696535
이 코드는 일반 JSON.stringify와 같은 다른 값에 대해 작동하므로 직렬화 된 객체가 맵이어야한다는 가정이 없습니다. 또한 배열이나 객체에 깊이 중첩 된 Map 일 수도 있습니다.
참고 URL : https://stackoverflow.com/questions/28918232/how-do-i-persist-a-es6-map-in-localstorage-or-elsewhere
'Program Tip' 카테고리의 다른 글
httptest를 사용하여 Go에서 http 호출을 테스트하는 방법 (0) | 2020.12.01 |
---|---|
for 또는 while없이 생성자의 무한 루프 (0) | 2020.12.01 |
pylint logging-not-lazy를 수정하는 방법은 무엇입니까? (0) | 2020.12.01 |
JavaScript로 CSS를 추가 하시겠습니까? (0) | 2020.12.01 |
내 빌드 서버의 배포 패키지에 추가 어셈블리가있는 이유는 무엇입니까? (0) | 2020.12.01 |