将对象存储在localStorage中

时间:2016-10-30 19:20:39

标签: javascript local-storage

// Extending the Storage object:
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);
}



// START HERE:

var dog = { 
   name : '',
   woof : function(){ console.log('woof woof, I am ' + this.name); }
}
dog.name = 'Lovely';

//Store in the localstorage
localStorage.setObject('mydog', dog); 

//Get from the localstorage
var mydog = localStorage.getObject('mydog');
console.log(mydog.name); // prints 'Lovely'
console.log(mydog.woof()); // ootest.html:36 Uncaught TypeError: mydog.woof is not a function(…)

为什么我收到错误?

2 个答案:

答案 0 :(得分:2)

LocalStorage仅支持字符串。您可以使用JSON编码存储对象,但这对您的函数不起作用。

JSON仅支持数字,字符串,布尔值,数组和基本对象结构。

答案 1 :(得分:1)

正确的方法:使用对象构造函数

let Dog = function(name) {
  this.name = name
}

Dog.prototype.woof = function woof() {
  console.log('woof woof, I am ' + this.name);
}

// Create a 'static' method for a Dog 'class'
Dog.fromJSON = function fromJSON(json) {
 return new Dog(JSON.parse(json).name)
}

// Instantinate your objects with `new` keyword
let dog = new Dog('Lovely')
dog.woof()

let storedDog = JSON.stringify(dog)
console.log('stored:', storedDog)

// Dog.fromJSON() returns new Dog object,
// with the same parameters for a constructor
// and with all methods
let restoredDog = Dog.fromJSON(storedDog)
restoredDog.woof()

限制

这种方法仅适用于与使用类似构造函数参数创建的行为相似的对象。如果您想在其中存储包含丰富数据的对象,请参阅How come JSON can't save object's functions?接受的答案。

仅仅是为了学习新东西:在JSON中存储函数

可以通过Function对象在运行时从字符串创建函数。要创建函数,我们需要传递参数和正文:

new Function ([arg1[, arg2[, ...argN]],] functionBody)

要获取方法params和body,我们需要在方法上调用继承的toString()方法:

dog.woof.toString()

能在字符串中存储功能声明,能从字符串创建功能。接下来的步骤是:将常规函数存储实现为对象属性,实现从JSON字符串恢复保存的函数。

下面的代码段中的脏工作实现示例。

为什么不应该实现这个?

安全风险。有人可以使用任意代码破解序列化函数。

糟糕的代码设计。拥有没有预定义构造函数的对象会导致维护地狱,因为你不能用javascript duck typing来对对象行为做出假设。

版本控制。如果更新代码,则无法确定客户端上存储的对象版本。

let dog = { 
   name : '',
   woof : function() {
     console.log('woof woof, I am ' + this.name);
   }
}
dog.name = 'Lovely';
dog.woof()

let storedDog = saveWithFuncToJSON(dog)
console.log('stored:', storedDog)

let restoredDog = restoreWithFuncFromJSON(storedDog)
restoredDog.woof()

console.log("Don't use this approach in production!")

// Code below is created only for fun,
// if you need this code in production,
// then your code smells

// Return JSON string
function saveWithFuncToJSON(object) {
  // Use Object.assign() just for simplicity,
  // something more solid needed for real object copying
  let preparedObject = Object.assign({}, object)

  for (let prop in preparedObject) {
    if (typeof(preparedObject[prop]) !== 'function') continue
	
    // Different platforms constructing function string in different ways
    // so you'll have to put a lot of efforts to make it work stable
    let funcStr = preparedObject[prop].toString()

    let startParams = funcStr.indexOf('(') + 1
    let endParams = funcStr.indexOf(')')
    let hasParams = (endParams - startParams)

    let funcParams = !hasParams ? [] : funcStr.slice(
      funcStr.indexOf('(') + 1,
      funcStr.indexOf('\n')
    ).split(',')

    let funcBody = funcStr.slice(
      funcStr.indexOf('{') + 1,
      funcStr.lastIndexOf('}')
    )
	
    // This is the most interesting part
    // We will store function as a string like freezing humans
    preparedObject[`__${prop}Func`] = {
      params: funcParams,
      body: funcBody
    }
  }

  return JSON.stringify(preparedObject)
}
      
function restoreWithFuncFromJSON(jsonSting) {
  let object = JSON.parse(jsonSting)

  for (let prop in object) {
    // Functions to be restored should be named differently
    let shouldConvertToFunc = prop.startsWith('__') && prop.endsWith('Func')
    if (!shouldConvertToFunc) continue
  
    let funcName = prop.slice(2, -4)
    let funcData = object[prop]
    let contructorArgsArray = funcData.params.concat([funcData.body])

    // Function() does all work for us
    object[funcName] = Function.apply(null, contructorArgsArray)
    delete object[prop]
  }

  return object;
}