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 , MozillaMozilla 다시 문서를 살펴보면 기능은 문자열 키 / 값 쌍만 처리하도록 제한되는 것 같습니다.

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

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.