1
// 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(...)

Why I am getting that error ?

asked Oct 30, 2016 at 19:20
3
  • You treat woof as a function: console.log( mydog.woof-->()<-- ), and it isn't... so you get an error. Commented Oct 30, 2016 at 19:23
  • @Dekel woof very much is a function.... Commented Oct 30, 2016 at 19:24
  • Just fyi, extending native prototypes should be avoided. Plenty of Info on SO id you wish to know the reasons. Commented Oct 30, 2016 at 21:31

2 Answers 2

2

The right way: use object constructor

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

Limitation

This approach will work well only for objects than will behave similar if created with similar constructor parameter. If you want to store objects with rich data inside, please refer to How come JSON can't save object's functions? accepted answer.

Just for learning something new: storing functions in JSON

Functions can be created from string at runtime via Function object. To create function it we need to pass arguments and body:

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

To get a method params and body we need to call inherited toString() method on method:

dog.woof.toString()

We can store function declaration in string and we can create function from string. Next steps are: implement conventional functions storage as object properties, implement restoring saved functions from JSON string.

Dirty working implementation example in the snippet below.

Why you should not implement this?

Security risk. Somebody could hack the serialised functions with any arbitrary code.

Bad code design. Having objects without predefined constructors leads to maintenance hell, because you can't you javascript duck typing to make assumptions about object behavior.

Versioning. If you update you code, you can't be sure about which version of objects stored on clients.

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

answered Oct 30, 2016 at 20:16
Sign up to request clarification or add additional context in comments.

Comments

2

LocalStorage only supports strings. You can store objects with JSON-encoding, but that won't work for your functions.

JSON only supports numbers, strings, booleans, arrays, and basic object structures.

answered Oct 30, 2016 at 19:24

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.