HTML5 localStorage에 객체 저장


2510

HTML5에 JavaScript 객체를 저장하고 싶지만 localStorage객체가 문자열로 변환되고있는 것 같습니다.

를 사용하여 기본 JavaScript 유형 및 배열을 저장하고 검색 할 수 localStorage있지만 객체가 작동하지 않는 것 같습니다. 그들은해야합니까?

내 코드는 다음과 같습니다.

var testObject = { 'one': 1, 'two': 2, 'three': 3 };
console.log('typeof testObject: ' + typeof testObject);
console.log('testObject properties:');
for (var prop in testObject) {
    console.log('  ' + prop + ': ' + testObject[prop]);
}

// Put the object into storage
localStorage.setItem('testObject', testObject);

// Retrieve the object from storage
var retrievedObject = localStorage.getItem('testObject');

console.log('typeof retrievedObject: ' + typeof retrievedObject);
console.log('Value of retrievedObject: ' + retrievedObject);

콘솔 출력은

typeof testObject: object
testObject properties:
  one: 1
  two: 2
  three: 3
typeof retrievedObject: string
Value of retrievedObject: [object Object]

setItem메소드가 입력을 저장하기 전에 문자열로 변환하는 것처럼 보입니다 .

Safari, Chrome 및 Firefox에서이 동작을 확인하므로 브라우저 별 버그 나 제한이 아니라 HTML5 웹 저장소 사양을 잘못 이해했다고 가정합니다 .

http://www.w3.org/TR/html5/infrastructure.html에 설명 된 구조화 된 클론 알고리즘 을 이해하려고했습니다 . 나는 그것이 말하는 것을 완전히 이해하지 못하지만 아마도 내 문제는 객체의 속성이 열거 할 수없는 것과 관련이있을 수 있습니다 (???)

쉬운 해결 방법이 있습니까?


업데이트 : W3C는 결국 구조적 복제 사양에 대한 생각을 바꾸고 구현과 일치하도록 사양을 변경하기로 결정했습니다. https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111을 참조 하십시오 . 따라서이 질문은 더 이상 100 % 유효하지 않지만 여전히 관심이있을 수 있습니다.


17
BTW, "structured clone algorithm"을 읽는 것이 정확합니다. 구현이 끝난 후에 스펙이 문자열 전용 값에서 이것으로 변경되었다는 것입니다. 이 문제를 추적하기 위해 버그 bugzilla.mozilla.org/show_bug.cgi?id=538142 를 mozilla와 함께 제출 했습니다.
— Nickolay

2
이것은 indexedDB에 대한 직업처럼 보인다 ...
— markasoftware

1
localStorage에 객체 배열을 저장하는 것은 어떻습니까? 문자열로 변환되는 것과 동일한 문제에 직면하고 있습니다.
— Jayant Pareek 2012 년

1
대신 배열을 직렬화 할 수 있습니까? JSON stringify로 저장 한 다음로드 할 때 다시 구문 분석합니까?
— Brandito

1
localDataStorage 를 사용 하여 자바 스크립트 데이터 유형 (Array, Boolean, Date, Float, Integer, String 및 Object)을 투명하게 저장할 수 있습니다.
— Mac

답변:


3169

Apple , Mozilla 및 Mozilla 다시 문서를 살펴보면 기능은 문자열 키 / 값 쌍만 처리하도록 제한되는 것 같습니다.

해결 방법은 객체를 저장하기 전에 문자열 화 한 다음 나중에 검색 할 때 구문 분석하는 것입니다.

var testObject = { 'one': 1, 'two': 2, 'three': 3 };

// Put the object into storage
localStorage.setItem('testObject', JSON.stringify(testObject));

// Retrieve the object from storage
var retrievedObject = localStorage.getItem('testObject');

console.log('retrievedObject: ', JSON.parse(retrievedObject));

159
모든 메타 데이터가 제거됩니다. 키-값 쌍이있는 객체 만 가져 오므로 동작이있는 객체를 다시 빌드해야합니다.
— oligofren

5
데이터가 용량을 초과하면 @CMS에서 setItem에서 예외가 발생합니까?
— Ashish Negi

3
... 순환 참조가있는 객체에만 적용 JSON.stringify()되며, 참조한 객체를 우리가 스트링 화하는 객체에서 전체 "콘텐츠"(암시 적으로 문자열 화)로 확장합니다. 참조 : stackoverflow.com/a/12659424/2044940
— CodeManX

3
이 방법의 문제점은 큰 배열이나 객체를 처리해야하는 경우 성능 문제입니다.
— Mark

3
@oligofren true, 그러나 maja가 eval () =>을 올바르게 제안했듯이 이것은 이것의 좋은 사용법 중 하나입니다. 쉽게 함수 코드를 검색 할 수 있습니다 => 문자열로 저장 한 다음 eval () 다시 :)
— jave.web

621

변형 에 대한 사소한 개선 :

Storage.prototype.setObject = function(key, value) {
    this.setItem(key, JSON.stringify(value));
}

Storage.prototype.getObject = function(key) {
    var value = this.getItem(key);
    return value && JSON.parse(value);
}

때문에의 단락 회로 평가 , getObject()됩니다 즉시 반환 할 null경우 key저장에 있지 않습니다. 또한 (빈 문자열; 처리 할 수없는 SyntaxError경우 value) 예외를 throw 하지 않습니다.""JSON.parse()


48
나는 즉시 나에게 명확하지 않은 사용법을 빠르게 추가하고 싶다. var userObject = { userId: 24, name: 'Jack Bauer' }; 그리고 그것을 설정하려면 localStorage.setObject('user', userObject); 스토리지에서 다시 가져 오기 userObject = localStorage.getObject('user'); 원하는 경우 객체 배열을 저장할 수도 있습니다.
— zuallauz

8
그냥 부울 식입니다. 두 번째 부분은 왼쪽 부분이 참인 경우에만 평가됩니다. 이 경우 전체 표현의 결과는 오른쪽 부분에서 나옵니다. 부울식이 평가되는 방식에 따라 인기있는 기술입니다.
— 구리 아

4
로컬 변수의 요점과 바로 가기 평가는 여기에 없습니다 (사소한 성능 향상 제외). 경우 key로컬 저장소에없는, window.localStorage.getItem(key)반환 null- 그것은 않습니다 하지 "잘못된 접근"예외를 던질 - 그리고 JSON.parse(null)반환 null뿐만 아니라 - 그것은 않습니다 되지 중 하나 예외를 발생, 크롬 21도별로도 5.1 섹션 15.12.2 ES 때문에 String(null) === "null"하는 캔 JSON 리터럴 로 해석됩니다 .
— PointedEars

6
로컬 저장소의 값은 항상 기본 문자열 값입니다. 이 바로 가기 평가가 처리하는 것은 누군가가 ""전에 빈 문자열을 저장했을 때 입니다. 예외를 발생 시키는 falseand 로 타입 변환되기 때문에 호출되지 않습니다. JSON.parse("")SyntaxError
— PointedEars

2
IE8에서는 작동하지 않으므로 지원 해야하는 경우 확인 된 답변의 기능을 사용하는 것이 좋습니다.
— Ezeke

220

다음과 같은 편리한 방법으로 Storage 객체를 확장하는 것이 유용 할 수 있습니다.

Storage.prototype.setObject = function(key, value) {
    this.setItem(key, JSON.stringify(value));
}

Storage.prototype.getObject = function(key) {
    return JSON.parse(this.getItem(key));
}

이렇게하면 API 아래에서 문자열 만 지원하더라도 실제로 원하는 기능을 얻을 수 있습니다.


13
CMS 접근 방식을 함수로 감싸는 것은 좋은 생각입니다 .JSON.stringify, JSON.parse, localStorage가 실제로 객체를 설정하고 검색 할 수 있는지 테스트하는 기능 테스트 만 있으면됩니다. 호스트 객체를 수정하는 것은 좋지 않습니다. 오히려 이것을 별도의 방법으로 보지 말고 localStorage.setObject.
— Garrett

4
이 getObject()던져 것이다 SyntaxError저장된 값 인 경우 예외가 ""있기 때문에, JSON.parse()캔가 처리하지. 자세한 내용은 Guria의 답변을 편집하십시오.
— PointedEars

9
내 두 센트지만 벤더가 제공 한 객체를 이와 같이 확장하는 것은 좋은 생각이 아닙니다.
— Sethen


73

스토리지 객체를 확장하는 것은 훌륭한 솔루션입니다. 내 API의 경우 localStorage에 대한 파사드를 만든 다음 설정 및 가져 오는 동안 객체인지 여부를 확인했습니다.

var data = {
  set: function(key, value) {
    if (!key || !value) {return;}

    if (typeof value === "object") {
      value = JSON.stringify(value);
    }
    localStorage.setItem(key, value);
  },
  get: function(key) {
    var value = localStorage.getItem(key);

    if (!value) {return;}

    // assume it is an object that has been stringified
    if (value[0] === "{") {
      value = JSON.parse(value);
    }

    return value;
  }
}

1
이것은 거의 내가 필요한 것입니다. 주석 앞에 if (value == null) {return false}를 추가해야했습니다. 그렇지 않으면 localStorage에 키가 있는지 확인할 때 오류가 발생했습니다.
— Francesco Frapporti

2
이것은 실제로 매우 시원합니다. @FrancescoFrapporti에 동의하면 null 값에 if가 필요합니다. 나는 또한 '|| value [0] == "[" '안에 배열이있는 경우 테스트합니다.
— rob_james

좋은 지적이다, 나는 이것을 편집 할 것이다. null 부분은 필요하지 않지만 필요한 경우 three ===을 권장합니다. JSHint 또는 JSLint를 사용하면 == 사용에 대한 경고가 표시됩니다.
— Alex Grande

3
그리고 닌자가 아닌 사람 (나 같은 사람) 에게이 대답에 대한 사용 예를 제공 할 수 있습니까? 그것은이다 data.set('username': 'ifedi', 'fullname': { firstname: 'Ifedi', lastname: 'Okonkwo'});?
— Ifedi Okonkwo

네 확실합니다! 숟가락으로 먹이고 싶다는 소망을 극복 할 때 코드를 테스트 해 보았습니다. 1) 허용되는 답변과 달리 문자열 데이터를 확인하는 데 시간이 걸리고 2) 다음과 달리 기본 객체를 확장하지 않기 때문에이 답변이 훌륭하다고 생각합니다.
— Ifedi Okonkwo

64

Stringify는 모든 문제를 해결하지는 못합니다

여기에있는 답변은 JavaScript에서 가능한 모든 유형을 다루지 않는 것 같습니다. 따라서 올바르게 처리하는 방법에 대한 간단한 예가 있습니다.

//Objects and Arrays:
    var obj = {key: "value"};
    localStorage.object = JSON.stringify(obj);  //Will ignore private members
    obj = JSON.parse(localStorage.object);
//Boolean:
    var bool = false;
    localStorage.bool = bool;
    bool = (localStorage.bool === "true");
//Numbers:
    var num = 42;
    localStorage.num = num;
    num = +localStorage.num;    //short for "num = parseFloat(localStorage.num);"
//Dates:
    var date = Date.now();
    localStorage.date = date;
    date = new Date(parseInt(localStorage.date));
//Regular expressions:
    var regex = /^No\.[\d]*$/i;     //usage example: "No.42".match(regex);
    localStorage.regex = regex;
    var components = localStorage.regex.match("^/(.*)/([a-z]*)$");
    regex = new RegExp(components[1], components[2]);
//Functions (not recommended):
    function func(){}
    localStorage.func = func;
    eval( localStorage.func );      //recreates the function with the name "func"

eval()악의가 보안, 최적화 및 디버깅과 관련된 문제로 이어질 수 있으므로 함수를 저장 하지 않는 것이 좋습니다 . 일반적으로 eval()JavaScript 코드에서는 절대 사용해서는 안됩니다.

개인 회원

JSON.stringify()객체 저장에 사용 하는 데 따른 문제점 은이 함수가 개인 멤버를 직렬화 할 수 없다는 것입니다. 이 문제는 .toString()웹 저장소에 데이터를 저장할 때 암시 적으로 호출되는 메서드 를 덮어 써서 해결할 수 있습니다 .

//Object with private and public members:
    function MyClass(privateContent, publicContent){
        var privateMember = privateContent || "defaultPrivateValue";
        this.publicMember = publicContent  || "defaultPublicValue";

        this.toString = function(){
            return '{"private": "' + privateMember + '", "public": "' + this.publicMember + '"}';
        };
    }
    MyClass.fromString = function(serialisedString){
        var properties = JSON.parse(serialisedString || "{}");
        return new MyClass( properties.private, properties.public );
    };
//Storing:
    var obj = new MyClass("invisible", "visible");
    localStorage.object = obj;
//Loading:
    obj = MyClass.fromString(localStorage.object);

순환 참조

stringify처리 할 수없는 또 다른 문제 는 순환 참조입니다.

var obj = {};
obj["circular"] = obj;
localStorage.object = JSON.stringify(obj);  //Fails

이 예제에서는 "Converting circle structure to JSON"을JSON.stringify() 던집니다 . 순환 참조 저장이 지원되어야하는 경우, 두 번째 매개 변수 가 사용될 수 있습니다.TypeError JSON.stringify()

var obj = {id: 1, sub: {}};
obj.sub["circular"] = obj;
localStorage.object = JSON.stringify( obj, function( key, value) {
    if( key == 'circular') {
        return "$ref"+value.id+"$";
    } else {
        return value;
    }
});

그러나 순환 참조를 저장하기위한 효율적인 솔루션을 찾는 것은 해결해야 할 작업에 따라 크게 달라 지므로 이러한 데이터를 복원하는 것도 쉽지 않습니다.

이 문제를 해결 하기 위해 SO에 대한 몇 가지 질문이 이미 있습니다. 순환 참조가있는 JavaScript 객체를 Stringify (JSON으로 변환)


2
따라서 데이터를 스토리지에 저장하는 것은 단순한 데이터 사본 의 전제를 기반으로해야합니다 . 살아있는 물체가 아닙니다.
— Roko C. Buljan

51

많은 솔루션을 포함하는 훌륭한 라이브러리가 있으므로 jStorage 라는 오래된 브라우저도 지원합니다.

당신은 개체를 설정할 수 있습니다

$.jStorage.set(key, value)

그리고 쉽게 검색

value = $.jStorage.get(key)
value = $.jStorage.get(key, "default value")

2
@SuperUberDuper jStorage는 프로토 타입, MooTools 또는 jQuery가 필요합니다
— JProgrammer

28

이론적으로 함수를 사용하여 객체를 저장할 수 있습니다.

function store (a)
{
  var c = {f: {}, d: {}};
  for (var k in a)
  {
    if (a.hasOwnProperty(k) && typeof a[k] === 'function')
    {
      c.f[k] = encodeURIComponent(a[k]);
    }
  }

  c.d = a;
  var data = JSON.stringify(c);
  window.localStorage.setItem('CODE', data);
}

function restore ()
{
  var data = window.localStorage.getItem('CODE');
  data = JSON.parse(data);
  var b = data.d;

  for (var k in data.f)
  {
    if (data.f.hasOwnProperty(k))
    {
      b[k] = eval("(" + decodeURIComponent(data.f[k]) + ")");
    }
  }

  return b;
}

그러나 함수 직렬화 / 역 직렬화 는 구현에 따라 다르므로 신뢰할 수 없습니다 .


1
함수 직렬화 / 역 직렬화 는 구현에 따라 다르므로 신뢰할 수 없습니다 . 또한, 교체 할 c.f[k] = escape(a[k]); 유니 코드 금고 c.f[k] = encodeURIComponent(a[k]);와 eval('b.' + k + ' = ' + unescape(data.f[k]));함께 b[k] = eval("(" + decodeURIComponent(data.f[k]) + ")");. 함수가 올바르게 직렬화 된 경우 익명 일 가능성이 있기 때문에 괄호가 필요합니다. 이는 유효한 / Statement / ( 그렇지 않으므로 eval()) SyntaxError예외를 throw합니다 ).
— PointedEars

그리고 typeof입니다 연산자 는 함수 인 것처럼, 그것을 쓰지 않는다. 교체 typeof(a[k])와 함께 typeof a[k].
— PointedEars

내 제안을 적용하고 접근 방식의 신뢰성을 강조하는 것 외에도 다음 버그를 수정했습니다. 1. 모든 변수가 선언 된 것은 아닙니다. 2. for- in자신의 속성에 대해 필터링되지 않았습니다. 3. 참조를 포함한 코드 스타일이 일치하지 않습니다.
— PointedEars

@ 실질적인 차이점은 무엇입니까? 사양에 the use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent. 따라 기능상의 차이가 보이지 않습니다.
— Michael

@Michael 인용 한 부분은로 시작합니다 Note *in particular* that …. 그러나 반환 값 사양은 다음과 An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration.같이 시작합니다 . 반환 값은 적합 구현을 function foo () {}가정합니다 .
— PointedEars

22

나는 '로컬 스토리지에 어레이를 저장하는 방법?'이라는 제목의 복제본으로 닫힌 다른 게시물을 치고 나서이 게시물에 도착했습니다. 어떤 스레드도 실제로 localStorage에서 배열을 유지 관리하는 방법에 대한 완전한 대답을 제공하지 않는 한 괜찮습니다. 그러나 두 스레드에 포함 된 정보를 기반으로 솔루션을 만들었습니다.

따라서 다른 사람이 배열 내에서 항목을 푸시 / 팝 / 시프트 할 수 있고 localStorage 또는 실제로 sessionStorage에 저장된 배열을 원하면 여기로 이동하십시오.

Storage.prototype.getArray = function(arrayName) {
  var thisArray = [];
  var fetchArrayObject = this.getItem(arrayName);
  if (typeof fetchArrayObject !== 'undefined') {
    if (fetchArrayObject !== null) { thisArray = JSON.parse(fetchArrayObject); }
  }
  return thisArray;
}

Storage.prototype.pushArrayItem = function(arrayName,arrayItem) {
  var existingArray = this.getArray(arrayName);
  existingArray.push(arrayItem);
  this.setItem(arrayName,JSON.stringify(existingArray));
}

Storage.prototype.popArrayItem = function(arrayName) {
  var arrayItem = {};
  var existingArray = this.getArray(arrayName);
  if (existingArray.length > 0) {
    arrayItem = existingArray.pop();
    this.setItem(arrayName,JSON.stringify(existingArray));
  }
  return arrayItem;
}

Storage.prototype.shiftArrayItem = function(arrayName) {
  var arrayItem = {};
  var existingArray = this.getArray(arrayName);
  if (existingArray.length > 0) {
    arrayItem = existingArray.shift();
    this.setItem(arrayName,JSON.stringify(existingArray));
  }
  return arrayItem;
}

Storage.prototype.unshiftArrayItem = function(arrayName,arrayItem) {
  var existingArray = this.getArray(arrayName);
  existingArray.unshift(arrayItem);
  this.setItem(arrayName,JSON.stringify(existingArray));
}

Storage.prototype.deleteArray = function(arrayName) {
  this.removeItem(arrayName);
}

사용법 예-localStorage 배열에 간단한 문자열 저장 :

localStorage.pushArrayItem('myArray','item one');
localStorage.pushArrayItem('myArray','item two');

사용법 예-sessionStorage 배열에 객체 저장 :

var item1 = {}; item1.name = 'fred'; item1.age = 48;
sessionStorage.pushArrayItem('myArray',item1);

var item2 = {}; item2.name = 'dave'; item2.age = 22;
sessionStorage.pushArrayItem('myArray',item2);

배열을 조작하는 일반적인 방법 :

.pushArrayItem(arrayName,arrayItem); -> adds an element onto end of named array
.unshiftArrayItem(arrayName,arrayItem); -> adds an element onto front of named array
.popArrayItem(arrayName); -> removes & returns last array element
.shiftArrayItem(arrayName); -> removes & returns first array element
.getArray(arrayName); -> returns entire array
.deleteArray(arrayName); -> removes entire array from storage

이것은 localStorage 또는 sessionStorage에 저장된 배열을 조작하는 데 매우 편리한 방법이며, 끌어 당기는 것보다 훨씬 많은 크레딧을받을 자격이 있습니다. @Andy Lorenz 공유해 주셔서 감사합니다!
— Velojet



6

localDataStorage 를 사용 하여 자바 스크립트 데이터 형식 (Array, Boolean, Date, Float, Integer, String 및 Object)을 투명하게 저장할 수 있습니다 . 또한 가벼운 데이터 난독 화를 제공하고 문자열을 자동으로 압축하며 키 (이름) 별 쿼리 및 키별 값 (키) 값으로 쿼리를 용이하게하며 키 접두어를 사용하여 동일한 도메인 내에서 세그먼트 화 된 공유 스토리지를 시행 할 수 있습니다.

[DISCLAIMER] 본인은 [/ DISCLAIMER] 유틸리티의 저자입니다.

예 :

localDataStorage.set( 'key1', 'Belgian' )
localDataStorage.set( 'key2', 1200.0047 )
localDataStorage.set( 'key3', true )
localDataStorage.set( 'key4', { 'RSK' : [1,'3',5,'7',9] } )
localDataStorage.set( 'key5', null )

localDataStorage.get( 'key1' )   -->   'Belgian'
localDataStorage.get( 'key2' )   -->   1200.0047
localDataStorage.get( 'key3' )   -->   true
localDataStorage.get( 'key4' )   -->   Object {RSK: Array(5)}
localDataStorage.get( 'key5' )   -->   null

보시다시피 기본 값이 존중됩니다.


1
이것은 훌륭한 자원이며 필요한 것입니다. AngularJS를 사용하여 localStorage에 특정 자바 스크립트 객체를 저장 해야하는 이오니아 앱을 수행하고 있으며 지금까지 JSON.parse 및 JSON.stringify를 수행했지만 작동하지만 조금 더 성가시다. 이와 같은 유틸리티를 사용하십시오. 나는 그것을 시도 할 것입니다.
— Nmuta

4

다른 옵션은 기존 플러그인을 사용하는 것입니다.

예를 들어 persisto 는 localStorage / sessionStorage에 쉬운 인터페이스를 제공하고 양식 필드 (입력, 라디오 버튼 및 확인란)의 지속성을 자동화하는 오픈 소스 프로젝트입니다.

지속 기능

(면책 조항 : 나는 저자입니다.)


여전히 readme에서 작업하고 있지만 내 버전 에는 persisto처럼 jQuery 가 필요 하지 않지만 jQuery 요소 Object를 처리하기위한 대안을 제공합니다. 가까운 장래에 더 많은 작업을할수록 더 많은 jQuery 객체를 처리하고 지속적인 데이터와 같은 것을 유지하는 데 도움이 될 것입니다. 또한 더 간단한 솔루션을 제공하기 위해 +1! 또한 모든 전통적인 방법을 사용합니다 localStroage. exp : var lsh = new localStorageHelper(); lsh.setItem('bob', 'bill'); 이벤트도 포함합니다.
— SpYk3HH

4

ejson 을 사용하여 객체를 문자열로 저장할 수 있습니다 .

EJSON은 더 많은 유형을 지원하기위한 JSON 확장입니다. 모든 JSON 안전 유형과 다음을 지원합니다.

  • 날짜 (자바 스크립트 Date)
  • 이진 (자바 스크립트 Uint8Array또는 EJSON.newBinary 의 결과 )
  • 사용자 정의 유형 ( EJSON.addType 참조 . 예를 들어 Mongo.ObjectID 는 이런 방식으로 구현됩니다.)

모든 EJSON 직렬화도 유효한 JSON입니다. 예를 들어 날짜와 이진 버퍼가있는 객체는 EJSON에서 다음과 같이 직렬화됩니다.

{
  "d": {"$date": 1358205756553},
  "b": {"$binary": "c3VyZS4="}
}

다음은 ejson을 사용하는 localStorage 래퍼입니다.

https://github.com/UziTech/storage.js

정규 표현식 및 함수를 포함하여 래퍼에 일부 유형을 추가했습니다.


2

나는 단지 20 줄의 코드로 다른 최소한의 래퍼를 만들었습니다.

localStorage.set('myKey',{a:[1,2,5], b: 'ok'});
localStorage.has('myKey');   // --> true
localStorage.get('myKey');   // --> {a:[1,2,5], b: 'ok'}
localStorage.keys();         // --> ['myKey']
localStorage.remove('myKey');

https://github.com/zevero/simpleWebstorage


2

형식화 된 사용자가 형식화 된 속성을 설정하고 가져 오려는 경우 :

/**
 * Silly wrapper to be able to type the storage keys
 */
export class TypedStorage<T> {

    public removeItem(key: keyof T): void {
        localStorage.removeItem(key);
    }

    public getItem<K extends keyof T>(key: K): T[K] | null {
        const data: string | null =  localStorage.getItem(key);
        return JSON.parse(data);
    }

    public setItem<K extends keyof T>(key: K, value: T[K]): void {
        const data: string = JSON.stringify(value);
        localStorage.setItem(key, data);
    }
}

사용법 예 :

// write an interface for the storage
interface MyStore {
   age: number,
   name: string,
   address: {city:string}
}

const storage: TypedStorage<MyStore> = new TypedStorage<MyStore>();

storage.setItem("wrong key", ""); // error unknown key
storage.setItem("age", "hello"); // error, age should be number
storage.setItem("address", {city:"Here"}); // ok

const address: {city:string} = storage.getItem("address");

2

https://github.com/adrianmay/rhaboo 는 다음과 같이 쓸 수있는 localStorage 설탕 레이어입니다.

var store = Rhaboo.persistent('Some name');
store.write('count', store.count ? store.count+1 : 1);
store.write('somethingfancy', {
  one: ['man', 'went'],
  2: 'mow',
  went: [  2, { mow: ['a', 'meadow' ] }, {}  ]
});
store.somethingfancy.went[1].mow.write(1, 'lawn');

JSON.stringify / parse는 큰 객체에서 정확하지 않고 느리기 때문에 사용하지 않습니다. 대신, 각 터미널 값에는 자체 localStorage 항목이 있습니다.

내가 rhaboo와 관련이 있다고 생각할 수 있습니다.


1

여기 @danott에 의해 게시 된 코드의 확장 버전

또한 로컬 저장소에서 삭제 값을 구현 하고 대신 Getter 및 Setter 레이어를 추가하는 방법을 보여줍니다.

localstorage.setItem(preview, true)

당신은 쓸 수 있습니다

config.preview = true

좋아 여기에 갔다 :

var PT=Storage.prototype

if (typeof PT._setItem >='u') PT._setItem = PT.setItem;
PT.setItem = function(key, value)
{
  if (typeof value >='u')//..ndefined
    this.removeItem(key)
  else
    this._setItem(key, JSON.stringify(value));
}

if (typeof PT._getItem >='u') PT._getItem = PT.getItem;
PT.getItem = function(key)
{  
  var ItemData = this._getItem(key)
  try
  {
    return JSON.parse(ItemData);
  }
  catch(e)
  {
    return ItemData;
  }
}

// Aliases for localStorage.set/getItem 
get =   localStorage.getItem.bind(localStorage)
set =   localStorage.setItem.bind(localStorage)

// Create ConfigWrapperObject
var config = {}

// Helper to create getter & setter
function configCreate(PropToAdd){
    Object.defineProperty( config, PropToAdd, {
      get: function ()      { return (  get(PropToAdd)      ) },
      set: function (val)   {           set(PropToAdd,  val ) }
    })
}
//------------------------------

// Usage Part
// Create properties
configCreate('preview')
configCreate('notification')
//...

// Config Data transfer
//set
config.preview = true

//get
config.preview

// delete
config.preview = undefined

글쎄, 당신은 별칭으로 부분을 제거 할 수 있습니다 .bind(...). 그러나 나는 이것에 대해 정말로 알고 있기 때문에 그것을 넣었습니다. 왜 단순한 get = localStorage.getItem;것이 효과가 없는지 알아 내기 위해 몇 시간이 걸렸습니다.


1

기존 Storage 객체를 손상 시키지는 않지만 래퍼를 생성하여 원하는 것을 수행 할 수있는 것을 만들었습니다. 결과는 객체처럼 액세스 할 수있는 메소드가없는 일반 객체입니다.

내가 만든 것.

1 localStorage속성을 마술처럼 만들고 싶다면 :

var prop = ObjectStorage(localStorage, 'prop');

여러 개가 필요한 경우 :

var storage = ObjectStorage(localStorage, ['prop', 'more', 'props']);

모든 작업 prop또는 내부 의 개체 storage가 자동으로 저장됩니다 localStorage. 당신은 항상 실제 물체로 놀고 있기 때문에 다음과 같은 일을 할 수 있습니다 :

storage.data.list.push('more data');
storage.another.list.splice(1, 2, {another: 'object'});

추적 된 개체 내부 의 모든 새 개체 는 자동으로 추적됩니다.

매우 큰 단점Object.observe() 은 브라우저 지원이 매우 제한 되어 있기 때문입니다. 그리고 곧 Firefox 나 Edge에서 출시 될 것 같지는 않습니다.


1

문자열 형식이 없으면 키 값을 저장할 수 없습니다 .

LocalStorage 는 키 / 값에 문자열 형식 만 지원합니다.

그렇기 때문에 배열 이나 객체 가 무엇이든 데이터를 문자열로 변환해야합니다 .

JSON.stringify () 메소드를 사용하여 localStorage에 데이터를 저장하려면 먼저 문자열 화하십시오 .

var myObj = [{name:"test", time:"Date 2017-02-03T08:38:04.449Z"}];
localStorage.setItem('item', JSON.stringify(myObj));

그런 다음 data를 검색하려면 String to Object를 다시 구문 분석해야합니다.

var getObj = JSON.parse(localStorage.getItem('item'));

도움이 되길 바랍니다.


0

객체를 저장하기 위해 문자열에서 객체로 객체를 가져 오는 데 사용할 수있는 글자를 만들 수 있습니다. 예를 들어

var obj = {a: "lol", b: "A", c: "hello world"};
function saveObj (key){
    var j = "";
    for(var i in obj){
        j += (i+"|"+obj[i]+"~");
    }
    localStorage.setItem(key, j);
} // Saving Method
function getObj (key){
    var j = {};
    var k = localStorage.getItem(key).split("~");
    for(var l in k){
        var m = k[l].split("|");
        j[m[0]] = m[1];
    }
    return j;
}
saveObj("obj"); // undefined
getObj("obj"); // {a: "lol", b: "A", c: "hello world"}

이 기법은 개체를 나누는 데 사용한 문자를 사용하는 경우 약간의 결함을 유발하며 매우 실험적입니다.


0

순환 참조가있는 객체에서 작동하게하는 방법을 찾았습니다.

순환 참조로 객체를 만들어 봅시다.

obj = {
    L: {
        L: { v: 'lorem' },
        R: { v: 'ipsum' }
    },
    R: {
        L: { v: 'dolor' },
        R: {
            L: { v: 'sit' },
            R: { v: 'amet' }
        }
    }
}
obj.R.L.uncle = obj.L;
obj.R.R.uncle = obj.L;
obj.R.R.L.uncle = obj.R.L;
obj.R.R.R.uncle = obj.R.L;
obj.L.L.uncle = obj.R;
obj.L.R.uncle = obj.R;

JSON.stringify순환 참조로 인해 여기서 할 수 없습니다 .

원형 삼촌

LOCALSTORAGE.CYCLICJSON가 .stringify및 .parse단지 정상처럼 JSON,하지만 순환 참조와 객체와 함께 작동합니다. ( "works"는 parse (stringify (obj))와 obj가 깊고 같으며 '내부 동등성'이 동일 함을 의미합니다)

그러나 바로 가기를 사용할 수 있습니다.

LOCALSTORAGE.setObject('latinUncles', obj)
recovered = LOCALSTORAGE.getObject('latinUncles')

다음 recovered과 같은 의미에서 obj와 "동일"합니다.

[
obj.L.L.v === recovered.L.L.v,
obj.L.R.v === recovered.L.R.v,
obj.R.L.v === recovered.R.L.v,
obj.R.R.L.v === recovered.R.R.L.v,
obj.R.R.R.v === recovered.R.R.R.v,
obj.R.L.uncle === obj.L,
obj.R.R.uncle === obj.L,
obj.R.R.L.uncle === obj.R.L,
obj.R.R.R.uncle === obj.R.L,
obj.L.L.uncle === obj.R,
obj.L.R.uncle === obj.R,
recovered.R.L.uncle === recovered.L,
recovered.R.R.uncle === recovered.L,
recovered.R.R.L.uncle === recovered.R.L,
recovered.R.R.R.uncle === recovered.R.L,
recovered.L.L.uncle === recovered.R,
recovered.L.R.uncle === recovered.R
]

구현은 다음과 같습니다. LOCALSTORAGE

LOCALSTORAGE = (function(){
  "use strict";
  var ignore = [Boolean, Date, Number, RegExp, String];
  function primitive(item){
    if (typeof item === 'object'){
      if (item === null) { return true; }
      for (var i=0; i<ignore.length; i++){
        if (item instanceof ignore[i]) { return true; }
      }
      return false;
    } else {
      return true;
    }
  }
  function infant(value){
    return Array.isArray(value) ? [] : {};
  }
  function decycleIntoForest(object, replacer) {
    if (typeof replacer !== 'function'){
      replacer = function(x){ return x; }
    }
    object = replacer(object);
    if (primitive(object)) return object;
    var objects = [object];
    var forest  = [infant(object)];
    var bucket  = new WeakMap(); // bucket = inverse of objects 
    bucket.set(object, 0);    
    function addToBucket(obj){
      var result = objects.length;
      objects.push(obj);
      bucket.set(obj, result);
      return result;
    }
    function isInBucket(obj){ return bucket.has(obj); }
    function processNode(source, target){
      Object.keys(source).forEach(function(key){
        var value = replacer(source[key]);
        if (primitive(value)){
          target[key] = {value: value};
        } else {
          var ptr;
          if (isInBucket(value)){
            ptr = bucket.get(value);
          } else {
            ptr = addToBucket(value);
            var newTree = infant(value);
            forest.push(newTree);
            processNode(value, newTree);
          }
          target[key] = {pointer: ptr};
        }
      });
    }
    processNode(object, forest[0]);
    return forest;
  };
  function deForestIntoCycle(forest) {
    var objects = [];
    var objectRequested = [];
    var todo = [];
    function processTree(idx) {
      if (idx in objects) return objects[idx];
      if (objectRequested[idx]) return null;
      objectRequested[idx] = true;
      var tree = forest[idx];
      var node = Array.isArray(tree) ? [] : {};
      for (var key in tree) {
        var o = tree[key];
        if ('pointer' in o) {
          var ptr = o.pointer;
          var value = processTree(ptr);
          if (value === null) {
            todo.push({
              node: node,
              key: key,
              idx: ptr
            });
          } else {
            node[key] = value;
          }
        } else {
          if ('value' in o) {
            node[key] = o.value;
          } else {
            throw new Error('unexpected')
          }
        }
      }
      objects[idx] = node;
      return node;
    }
    var result = processTree(0);
    for (var i = 0; i < todo.length; i++) {
      var item = todo[i];
      item.node[item.key] = objects[item.idx];
    }
    return result;
  };
  var console = {
    log: function(x){
      var the = document.getElementById('the');
      the.textContent = the.textContent + '\n' + x;
	},
	delimiter: function(){
      var the = document.getElementById('the');
      the.textContent = the.textContent +
		'\n*******************************************';
	}
  }
  function logCyclicObjectToConsole(root) {
    var cycleFree = decycleIntoForest(root);
    var shown = cycleFree.map(function(tree, idx) {
      return false;
    });
    var indentIncrement = 4;
    function showItem(nodeSlot, indent, label) {
      var leadingSpaces = ' '.repeat(indent);
      var leadingSpacesPlus = ' '.repeat(indent + indentIncrement);
      if (shown[nodeSlot]) {
        console.log(leadingSpaces + label + ' ... see above (object #' + nodeSlot + ')');
      } else {
        console.log(leadingSpaces + label + ' object#' + nodeSlot);
        var tree = cycleFree[nodeSlot];
        shown[nodeSlot] = true;
        Object.keys(tree).forEach(function(key) {
          var entry = tree[key];
          if ('value' in entry) {
            console.log(leadingSpacesPlus + key + ": " + entry.value);
          } else {
            if ('pointer' in entry) {
              showItem(entry.pointer, indent + indentIncrement, key);
            }
          }
        });
      }
    }
	console.delimiter();
    showItem(0, 0, 'root');
  };
  function stringify(obj){
    return JSON.stringify(decycleIntoForest(obj));
  }
  function parse(str){
    return deForestIntoCycle(JSON.parse(str));
  }
  var CYCLICJSON = {
    decycleIntoForest: decycleIntoForest,
    deForestIntoCycle : deForestIntoCycle,
    logCyclicObjectToConsole: logCyclicObjectToConsole,
    stringify : stringify,
    parse : parse
  }
  function setObject(name, object){
    var str = stringify(object);
    localStorage.setItem(name, str);
  }
  function getObject(name){
    var str = localStorage.getItem(name);
    if (str===null) return null;
    return parse(str);
  }
  return {
    CYCLICJSON : CYCLICJSON,
    setObject  : setObject,
    getObject  : getObject
  }
})();
obj = {
	L: {
		L: { v: 'lorem' },
		R: { v: 'ipsum' }
	},
	R: {
		L: { v: 'dolor' },
		R: {
			L: { v: 'sit' },
			R: { v: 'amet' }
		}
	}
}
obj.R.L.uncle = obj.L;
obj.R.R.uncle = obj.L;
obj.R.R.L.uncle = obj.R.L;
obj.R.R.R.uncle = obj.R.L;
obj.L.L.uncle = obj.R;
obj.L.R.uncle = obj.R;

// LOCALSTORAGE.setObject('latinUncles', obj)
// recovered = LOCALSTORAGE.getObject('latinUncles')
// localStorage not available inside fiddle ):
LOCALSTORAGE.CYCLICJSON.logCyclicObjectToConsole(obj)
putIntoLS = LOCALSTORAGE.CYCLICJSON.stringify(obj);
recovered = LOCALSTORAGE.CYCLICJSON.parse(putIntoLS);
LOCALSTORAGE.CYCLICJSON.logCyclicObjectToConsole(recovered);

var the = document.getElementById('the');
the.textContent = the.textContent + '\n\n' +
JSON.stringify(
[
obj.L.L.v === recovered.L.L.v,
obj.L.R.v === recovered.L.R.v,
obj.R.L.v === recovered.R.L.v,
obj.R.R.L.v === recovered.R.R.L.v,
obj.R.R.R.v === recovered.R.R.R.v,
obj.R.L.uncle === obj.L,
obj.R.R.uncle === obj.L,
obj.R.R.L.uncle === obj.R.L,
obj.R.R.R.uncle === obj.R.L,
obj.L.L.uncle === obj.R,
obj.L.R.uncle === obj.R,
recovered.R.L.uncle === recovered.L,
recovered.R.R.uncle === recovered.L,
recovered.R.R.L.uncle === recovered.R.L,
recovered.R.R.R.uncle === recovered.R.L,
recovered.L.L.uncle === recovered.R,
recovered.L.R.uncle === recovered.R
]
)
<pre id='the'></pre>


-2

localStorage.setItem ( 'user', JSON.stringify (user));

그런 다음 상점에서 검색하여 오브젝트로 다시 변환하십시오.

var user = JSON.parse (localStorage.getItem ( 'user'));

상점의 모든 항목을 삭제해야하는 경우 간단히 다음을 수행 할 수 있습니다.

localStorage.clear ();


3
이것은 10 살짜리 질문입니다. 귀하의 답변에 다른 답변에서 아직 다루지 않은 내용이 추가되었다고 생각하십니까?
— Kristopher Johnson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.