How can I serialize a function in JavaScript?
For example, say I have a function defined as follows:
function foo() {
return "Hello, serialized world!";
}
I want to be able to serialize that function and store it using localStorage
. How can I go about doing that?
Solution 1:
Most browsers (Chrome, Safari, Firefox, possibly others) return the definition of functions from the .toString()
method:
> function foo() { return 42; }
> foo.toString()
"function foo() { return 42; }"
Just be careful because native functions won't serialize properly. For example:
> alert.toString()
"function alert() { [native code] }"
Solution 2:
function foo() {
alert('native function');
return 'Hello, serialised world!';
}
Serializing
var storedFunction = foo.toString();
Deserializing
var actualFunction = new Function('return ' + foo.toString())()
Explanation
foo.toString() will be string version of the function foo
"function foo() { ... return 'Hello, serialised world!';}"
But new Function
takes the body of a function and not the function itself.
See MDN: Function
So we can create a function that returns us back this function and assign it to some variable.
"return function foo() { ... return 'Hello, serialised world!';}"
So now when we pass this string to the constructor we get a function and we immediately execute it to get back our original function. :)
Solution 3:
I made this answer to address some pretty big flaws with the existing answers: .toString()
/eval()
and new Function()
on their own wont work at all if your function uses this
or named arguments (function (named, arg) {}
), respectively.
Using toJSON()
below, all you need to do is call JSON.stringify()
as usual on the function, and use Function.deserialise
when parse()
ing.
The following wont work for concise functions (hello => 'there'
), but for standard ES5 fat functions it'll return it as it was defined, closures notwithstanding of course. My other answer will work with all that ES6 goodness.
Function.prototype.toJSON = function() {
var parts = this
.toString()
.match(/^\s*function[^(]*\(([^)]*)\)\s*{(.*)}\s*$/)
;
if (parts == null)
throw 'Function form not supported';
return [
'window.Function',
parts[1].trim().split(/\s*,\s*/),
parts[2]
];
};
Function.deserialise = function(key, data) {
return (data instanceof Array && data[0] == 'window.Function') ?
new (Function.bind.apply(Function, [Function].concat(data[1], [data[2]]))) :
data
;
};
Take a look at the DEMO
At it's simplest:
var test = function(where) { return 'hello ' + where; };
test = JSON.parse(JSON.stringify(test), Function.deserialise);
console.log(test('there'));
//prints 'hello there'
More usefully, you can serialise entire objects containing functions and pull them back out:
test = {
a : 2,
run : function(x, y, z) { return this.a + x + y + z; }
};
var serialised = JSON.stringify(test);
console.log(serialised);
console.log(typeof serialised);
var tester = JSON.parse(serialised, Function.deserialise);
console.log(tester.run(3, 4, 5));
Outputs:
{"a":2,"run":["window.Function",["x","y","z"]," return this.a + x + y + z; "]}
string
14
I didn't test older IE's, but it works on IE11, FF, Chrome, Edge.
NB, the name
of the function is lost, if you use that property then there's nothing you can do, really.
You can change it to not use prototype
easily, but that's for you to do if that's what you need.