【问题标题】:json.stringify does not process object methodsjson.stringify 不处理对象方法
【发布时间】:2013-08-07 23:34:36
【问题描述】:

我正在尝试开发一个可以在大多数现代浏览器(Chrome、Firefox、IE 9+、Safari、Opera)中运行的离线 HTML5 应用程序。由于 Safari(尚)不支持 IndexedDB,并且不推荐使用 WebSQL,因此我决定使用 localStorage 来存储用户生成的 JavaScript 对象,并使用JSON.stringify()/JSON.parse() 来放入或取出对象。但是,我发现JSON.stringify() 不处理方法。这是一个带有简单方法的示例对象:

    var myObject = {};
    myObject.foo = 'bar';
    myObject.someFunction = function () {/*code in this function*/}

如果我将此对象字符串化(然后将其放入 localStorage),将保留的只是myObject.foo,而不是myObject.someFunction()

    //put object into localStorage
    localStorage.setItem('myObject',JSON.stringify(myObject));

    //pull it out of localStorage and set it to myObject
    myObject = localStorage.getItem('myObject');

    //undefined!
    myObject.someFunction

我相信你们中的许多人可能已经知道这个限制/功能/无论您想怎么称呼它。我想出的解决方法是使用方法(myObject = new objectConstructor())创建一个对象,从 localStorage 中提取对象属性,并将它们分配给我创建的新对象。我觉得这是一种迂回的方法,但我是 JavaScript 世界的新手,所以这就是我解决它的方法。所以这是我的大问题:我希望将整个对象(属性+方法)包含在 localStorage 中。我该怎么做呢?如果你能告诉我一个更好的算法,或者我不知道的另一种 JSON 方法,我将不胜感激。

【问题讨论】:

  • JSON 中不存在函数。请参阅:json.org
  • 这就是我阅读规范后的想法,但是你有什么办法可以保存这些方法吗?还是我的方式你会怎么做?
  • JSON.stringify function的可能重复
  • 我认为使用构造函数是有意义的。毕竟,为什么要存储方法?数据使对象独一无二,方法只是处理数据的方法。我将添加一个从 JSON 字符串填充对象的方法。
  • 您的“解决方法”正是如何做到这一点。只序列化和存储数据,而不是整个对象。

标签: javascript json local-storage


【解决方案1】:

不按要求修复函数,而是一种在本地存储变量的方法...

<html>
  <head>
    <title>Blank</title>
    <script>
      if(localStorage.g===undefined) localStorage.g={};
      var g=JSON.parse(localStorage.g);
    </script>
  </head>
  <body>
    <input type=button onClick="localStorage.g=JSON.stringify(g, null, '  ')" value="Save">
    <input type=button onClick="g=JSON.parse(localStorage.g)" value="Load">
  </body>
</html>

将所有变量保留在对象 g 中。示例:

  g.arr=[1,2,3];
  • 注意某些类型,例如Date,您需要执行以下操作:
  g.date=new Date(g.date);
  • 每页本地存储:不同的页面有不同的gs

【讨论】:

    【解决方案2】:

    如果你想对你的对象进行字符串化,但它们有函数,你可以使用JSON.stringify()和第二个参数replacer。为了防止对对象的循环依赖,您可以使用var cache = []

    在我们的项目中,我们使用lodash。我们使用以下函数来生成日志。可用于将对象保存到localStorage

    var stringifyObj = function(obj) {
      var cache = []
      return JSON.stringify(obj, function(key, value) {
        if (
          _.isString(value) ||
          _.isNumber(value) ||
          _.isBoolean(value)
        ) {
          return value
        } else if (_.isError(value)) {
          return value.stack || ''
        } else if (_.isPlainObject(value) || _.isArray(value)) {
          if (cache.indexOf(value) !== -1) {
            return
          } else {
            // cache each item 
            cache.push(value)
            return value
          }
        }
      })
    }
    
    // create a circular object
    var circularObject = {}
    circularObject.circularObject = circularObject
    
    // stringify an object
    $('body').text(
      stringifyObj(
        {
          myBooblean: true,
          myString: 'foo',
          myNumber: 1,
          myArray: [1, 2, 3],
          myObject: {},
          myCircularObject: circularObject,
          myFunction: function () {}
        }
      )
    )
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

    【讨论】:

      【解决方案3】:

      javascript 中的函数不仅仅是它们的代码。他们也有范围。代码可以字符串化,但作用域不能。

      JSON.stringify() 将编码 JSON 支持的值。具有可以是对象、数组、字符串、数字和布尔值的值的对象。其他任何内容都将被忽略或抛出错误。函数不是 JSON 中受支持的实体。 JSON 只处理纯数据,函数不是数据,而是具有更复杂语义的行为。


      也就是说,您可以更改 JSON.stringify() 的工作方式。第二个参数是replacer 函数。因此,您可以通过强制对函数进行字符串化来强制执行您想要的行为:

      var obj = {
        foo: function() {
          return "I'm a function!";
        }
      };
      
      var json = JSON.stringify(obj, function(key, value) {
        if (typeof value === 'function') {
          return value.toString();
        } else {
          return value;
        }
      });
      
      console.log(json);
      // {"foo":"function () { return \"I'm a function!\" }"}
      

      但是当你读回来时,你必须评估函数字符串并将结果设置回对象,因为 JSON 不支持函数


      JSON 中的所有编码函数都会变得非常复杂。你确定你要这么做吗?可能有更好的方法...

      也许您可以改为保存原始数据,并将其从页面上加载的 JS 传递给构造函数。 localStorage 将只保存数据,但加载到页面上的代码将提供操作该数据的方法。

      // contrived example...
      
      var MyClass = function(data) {
        this.firstName = data.firstName;
        this.lastName = data.lastName;
      }
      
      MyClass.prototype.getName() {
        return this.firstName + ' ' + this.lastName;
      }
      
      localStorage.peopleData = [{
        firstName: 'Bob',
        lastName:  'McDudeFace'
      }];
      
      var peopleData = localStorage.peopleData;
      
      var bob = new MyClass(peopleData[0]);
      bob.getName() // 'Bob McDudeFace'
      

      我们不需要将getName() 方法保存到localStorage。我们只需要将这些数据输入到将提供该方法的构造函数中。

      【讨论】:

      • 好的,我明白你在说什么。我只是想看看是否有我遗漏的东西。我最终将只存储数据并将该数据传递给构造函数。感谢您清除它!
      • 优秀的答案!只是想我要补充一点,应该澄清人为的例子localStorage.peopleData = [{ firstName: 'Bob', lastName: 'McDudeFace' }];应该是:localStorage.peopleData = JSON.stringify( [{ firstName: 'Bob', lastName: 'McDudeFace' }] );也就是说,存储在localStorage中的对象数组需要转换为 JSON 字符串。
      • 当我尝试解析 JSON 时,出现此错误:myMethod is not a function ;为什么?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-30
      • 1970-01-01
      • 1970-01-01
      • 2013-02-13
      • 1970-01-01
      • 2018-03-20
      相关资源
      最近更新 更多