代码之家  ›  专栏  ›  技术社区  ›  Kristopher Johnson

在HTML5 localStorage中存储对象

  •  2230
  • Kristopher Johnson  · 技术社区  · 15 年前

    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%有效,但答案可能仍然很有趣。

    26 回复  |  直到 8 年前
        1
  •  4
  •   Moshiur Rahman    5 年前

    Apple , Mozilla Mozilla again 在文档中,该功能似乎仅限于处理字符串键/值对。

    解决方法可以是: 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));
    
        2
  •  1
  •   mathheadinclouds    5 年前

    一个小的改进 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);
    }
    

    因为 short-circuit evaluation , getObject() 立即 返回 null key 不在仓库里。它也不会扔垃圾 SyntaxError 例外情况如果 value "" (b)空字符串; JSON.parse() 无法处理)。

        3
  •  0
  •   Kamil Kiełczewski    4 年前

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

    通过这种方式,您可以获得真正想要的功能,即使API下面只支持字符串。

        4
  •  0
  •   Gabriel H    4 年前

    扩展存储对象是一个很棒的解决方案。对于我的API,我已经为localStorage创建了一个facade,然后在设置和获取时检查它是否是对象。

    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;
      }
    }
    
        5
  •  0
  •   Dan Loewenherz Snowman    4 年前

    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() is邪恶可能导致安全、优化和调试方面的问题。 一般来说 不应在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 无法处理循环引用:

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

    JSON.stringify() TypeError “将循环结构转换为JSON” . 如果应支持存储循环引用,则 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;
        }
    });
    

    然而,找到存储循环引用的有效解决方案在很大程度上取决于需要解决的任务,恢复此类数据也不是一件小事。

    关于如何处理这个问题,已经存在一些问题: Stringify (convert to JSON) a JavaScript object with circular reference

        6
  •  -3
  •   Samsul Islam Rute Figueiredo    4 年前

    jStorage

    您可以设置一个对象

    $.jStorage.set(key, value)
    

    而且很容易找到它

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