2875

JavaScript オブジェクトを HTML5 に保存したいのです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 Web Storage仕様に対する私の誤解によるものだと思います。

http://www.w3.org/TR/html5/infrastructure.htmlで説明されている構造化クローンアルゴリズムを理解しようとしました。私はそれが言っていることを完全には理解していませんが、おそらく私の問題は、オブジェクトのプロパティが列挙できないことに関係しています (???)

簡単な回避策はありますか?


更新: W3C は最終的に構造化クローン仕様について考えを変え、実装に合わせて仕様を変更することを決定しました。https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111を参照してください。したがって、この質問はもはや 100% 有効ではありませんが、回答は依然として興味深いものになる可能性があります。

4

24 に答える 24

3609

AppleMozilla、および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));
于 2010-01-06T04:25:58.127 に答える
661

バリアントのマイナーな改善:

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()すぐに返却されます。また、(空の文字列;それを処理できない)場合は例外をスローしません。nullkeySyntaxErrorvalue""JSON.parse()

于 2010-06-30T06:45:08.483 に答える
239

次の便利なメソッドを使用して 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 の下では文字列しかサポートしていませんが、本当に必要な機能を取得できます。

于 2010-01-06T04:42:47.643 に答える
81

Storage オブジェクトのファサードを作成することは素晴らしい解決策です。そうすれば、独自のメソッドを実装できgetますset。私の 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;
  }
}
于 2011-01-21T18:29:16.273 に答える
73

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()(Web ストレージにデータを保存するときに暗黙的に呼び出されます)。

//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対処できないもう 1 つの問題は、循環参照です。

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

この例では、 「循環構造を JSON に変換しています」JSON.stringify()をスローします。循環参照の保存をサポートする必要がある場合は、 の 2 番目のパラメーターを使用できます。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 オブジェクトを文字列化 (JSON に変換)

于 2014-11-19T09:51:42.870 に答える
55

jStorageと呼ばれる古いブラウザもサポートするように、多くのソリューションをラップする優れたライブラリがあります。

オブジェクトを設定できます

$.jStorage.set(key, value)

そして簡単に回収

value = $.jStorage.get(key)
value = $.jStorage.get(key, "default value")
于 2011-08-23T03:52:02.193 に答える
31

理論的には、関数を使用してオブジェクトを格納することができます。

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

ただし、関数のシリアライゼーション/デシリアライゼーションは実装依存であるため、信頼できません。

于 2011-04-05T21:20:04.493 に答える
16

ここで説明する機能の多くと互換性を向上させるために、抽象化ライブラリを使用することをお勧めします。多くのオプション:

于 2015-03-12T07:59:39.463 に答える
6

ejsonを使用して、オブジェクトを文字列として格納できます。

EJSON は、より多くの型をサポートするための JSON の拡張です。すべての JSON セーフ タイプに加えて、以下をサポートします。

  • 日付 (JavaScript Date)
  • バイナリ (JavaScriptUint8ArrayまたはEJSON.newBinaryの結果)
  • ユーザー定義型 ( EJSON.addTypeを参照してください。たとえば、Mongo.ObjectIDはこの方法で実装されています。)

すべての EJSON シリアライゼーションも有効な JSON です。たとえば、日付とバイナリ バッファーを持つオブジェクトは、EJSON で次のようにシリアル化されます。

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

ejson を使用した localStorage ラッパーを次に示します。

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

正規表現と関数を含むいくつかの型をラッパーに追加しました

于 2016-07-28T15:41:31.680 に答える
5

別のオプションは、既存のプラグインを使用することです。

たとえば、persistoは、localStorage/sessionStorage への簡単なインターフェイスを提供し、フォーム フィールド (入力、ラジオ ボタン、およびチェックボックス) の永続化を自動化するオープン ソース プロジェクトです。

永続化機能

(免責事項:私は著者です。)

于 2016-06-16T17:10:51.023 に答える
4

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 と関係があると推測できます。

于 2014-10-01T11:14:11.410 に答える
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

于 2016-09-28T10:30:11.397 に答える
1

私は、既存の Storage オブジェクトを破壊しないものを作成しましたが、ラッパーを作成して、やりたいことができるようにしました。結果は、通常のオブジェクトであり、メソッドはなく、他のオブジェクトと同様にアクセスできます。

私が作ったもの。

localStorage1 つのプロパティをマジックにしたい場合:

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

複数必要な場合:

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

に対して行ったすべての操作、または内部propのオブジェクトは、自動的に に保存されます。常に実際のオブジェクトで遊んでいるので、次のようなことができます。 storagelocalStorage

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

また、追跡されたオブジェクト内のすべての新しいオブジェクトが自動的に追跡されます。

非常に大きな欠点:に依存してObject.observe()いるため、ブラウザーのサポートが非常に限られています。また、Firefox や Edge にもすぐには対応しないようです。

于 2015-11-28T20:39:41.763 に答える