javascript - 초기화 - 자바스크립트 localstorage




HTML5 localStorage에 객체 저장 (18)

문자열이있는 모든 문제를 해결하지 않는다

여기에있는 답변이 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

이 예제에서 JSON.stringify()"원형 구조체를 JSON으로 변환" 하는 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가이 문제를 다루는 것에 대해 이미 질문이 있습니다. Circular Reference가있는 JavaScript 객체 Stringify (JSON으로 변환)

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

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

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

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 Web Storage 사양에 대한 오해라고 생각합니다.

나는 http://www.w3.org/TR/html5/infrastructure.html 설명 된 구조화 된 복제 알고리즘을 이해하려고 노력했다. 나는 그것이 무엇을 말하고 있는지 완전히 이해하지는 못했지만 내 문제는 열거되지 않는 내 물건의 속성과 관련이있다 (???)

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

업데이트 : W3C는 결국 구조화 된 복제 사양에 대한 생각을 바꾸고 구현을 일치시키기 위해 사양을 변경하기로 결정했습니다. https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111 참조 https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111 . 따라서이 질문은 더 이상 유효하지 않지만 질문에 대한 답변은 여전히 ​​중요 할 수 있습니다.


@Guria의 대답에 대한 개선 :

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


Storage.prototype.getObject = function (key) {
    var value = this.getItem(key);
    try {
        return JSON.parse(value);
    }
    catch(err) {
        console.log("JSON parse failed for lookup of ", key, "\n error was: ", err);
        return null;
    }
};

Storage 객체를 확장하는 것은 대단한 해결책입니다. 내 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;
  }
}

객체를 저장하려면 문자열에서 객체로 객체를 가져 오는 데 사용할 수있는 문자를 만들 수 있습니다 (의미가 없을 수도 있음). 예를 들어

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"}

이 기법을 사용하면 개체를 분할하는 데 사용한 문자를 사용하면 약간의 결함이 생기며 매우 실험적입니다.


내가 득표 한 답변 중 하나를 수정했습니다. 나는 그것이 필요하지 않으면 2 대신 하나의 기능을 갖는 팬이다.

Storage.prototype.object = function(key, val) {
    if ( typeof val === "undefined" ) {
        var value = this.getItem(key);
        return value ? JSON.parse(value) : null;
    } else {
        this.setItem(key, JSON.stringify(val));
    }
}

localStorage.object("test", {a : 1}); //set value
localStorage.object("test"); //get value

또한 값을 설정하지 않으면 false 대신 null 이 반환됩니다. false 는 의미를 가지지 만, null 은 그렇지 않습니다.


다음과 같은 편리한 방법으로 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 아래에서 문자열 만 지원할지라도 실제로 원하는 기능을 사용할 수 있습니다.


또한 다른 데이터 유형과 마찬가지로 객체 / 배열을 처리하기 위해 기본 저장소 setItem(key,value)getItem(key) 메소드를 대체 할 수 있습니다. 그렇게하면 보통처럼 localStorage.setItem(key,value)localStorage.getItem(key) 을 호출 할 수 있습니다.

이 문제를 광범위하게 테스트하지는 못했지만 작은 프로젝트에서 문제없이 작동하는 것처럼 보였습니다.

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

Storage.prototype._getItem = Storage.prototype.getItem;
Storage.prototype.getItem = function(key)
{  
  try
  {
    return JSON.parse(this._getItem(key));
  }
  catch(e)
  {
    return this._getItem(key);
  }
}

로컬 저장소에 JSON 객체 사용 :

//세트

var m={name:'Hero',Title:'developer'};
localStorage.setItem('us', JSON.stringify(m));

//도망

var gm =JSON.parse(localStorage.getItem('us'));
console.log(gm.name);

// 모든 로컬 저장 키와 값의 반복

for (var i = 0, len = localStorage.length; i < len; ++i) {
  console.log(localStorage.getItem(localStorage.key(i)));
}

// DELETE

localStorage.removeItem('us');
delete window.localStorage["us"];


연락처로부터받은 메시지를 추적하기 위해 localStorage를 사용하는 라이브러리의 작은 예 :

// This class is supposed to be used to keep a track of received message per contacts.
// You have only four methods:

// 1 - Tells you if you can use this library or not...
function isLocalStorageSupported(){
    if(typeof(Storage) !== "undefined" && window['localStorage'] != null ) {
         return true;
     } else {
         return false;
     }
 }

// 2 - Give the list of contacts, a contact is created when you store the first message
 function getContacts(){
    var result = new Array();
    for ( var i = 0, len = localStorage.length; i < len; ++i ) {
        result.push(localStorage.key(i));
    }
    return result;
 }

 // 3 - store a message for a contact
 function storeMessage(contact, message){
    var allMessages;
    var currentMessages = localStorage.getItem(contact);
    if(currentMessages == null){
        var newList = new Array();
        newList.push(message);
        currentMessages = JSON.stringify(newList);
    }
    else
    {
        var currentList =JSON.parse(currentMessages);
        currentList.push(message);
        currentMessages = JSON.stringify(currentList);
    }
    localStorage.setItem(contact, currentMessages);
 }

 // 4 - read the messages of a contact
 function readMessages(contact){

    var result = new Array();
    var currentMessages = localStorage.getItem(contact);

    if(currentMessages != null){
        result =JSON.parse(currentMessages);
    }
    return result;
 }

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

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;
}

그러나 함수의 serialization / deserialization 은 구현에 의존 하기 때문에 신뢰할 수 없습니다.


입력 된 속성을 설정하고 가져올 Typescript 사용자 :

/**
 * 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");

Apple , MozillaMicrosoft 설명서를 보면 기능은 문자열 키 / 값 쌍만 처리 할 수있는 것으로 제한됩니다.

임시 해결책은 오브젝트를 저장하기 전에 오브젝트를 stringify 다음 오브젝트를 검색 할 때 나중에 구문 분석하는 것입니다.

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));

http://rhaboo.org 는 다음과 같이 작성할 수있는 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와 관련이 있다고 생각할 수 있습니다 ;-)

애드리안.


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

EJSON은 더 많은 유형을 지원하기 위해 JSON을 확장 한 것입니다. 그것은 모든 JSON 안전한 유형을 지원뿐만 아니라 :

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

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

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

여기에 ejson을 사용하는 localStorage 래퍼가 있습니다.

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

정규 표현식과 함수를 포함한 래퍼에 몇 가지 유형을 추가했습니다.


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

[면책] 나는 유틸리티의 저자이다 [/ 부인]

예 :

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

보시다시피 원시 값은 존중됩니다.


variant 에 대한 사소한 개선 :

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);
}

단락 회로 평가로 인해, key 가 Storage에 null 경우, getObject()즉시 null 합니다. 또한 value"" (빈 문자열 인 경우 JSON.parse() 가 처리 할 수없는 경우) 인 경우 SyntaxError 예외를 throw하지 않습니다.


로컬, 세션, opendb 라이브러리를 사용할 수있는 쿠키에서 이러한 종류의 문제를 피하는 것 같습니다.

Ex-이 스 니펫을 사용하여이 문제를 해결할 수 있습니다.

// for set object in db
db.local.setJSON("key", {name: "xyz"});  

// for get object form db
db.local.getJSON("key");

https://github.com/pankajbisht/openDB

웹 저장소에 대한 자세한 내용은 웹 저장소 문서를 참조하십시오.





local-storage