47

I have an object that has some properties and methods, like so:

{name: "FirstName",
age: "19",
load: function () {},
uniq: 0.5233059714082628}

and I have to pass this object to another function. So I tried to use JSON.stringify(obj) but the load function (which of course isn't empty, this is just for the purpose of this example) is being "lost".

Is there any way to stringify and object and maintain the methods it has?

Thanks!

NoobEditor
  • 15,563
  • 19
  • 81
  • 112
Sorin Buturugeanu
  • 1,782
  • 4
  • 19
  • 32

6 Answers6

73

There is a way to serialize a function in JS, but you'll have to eval it on the other side and it will also lose access to it's original scope. A way to do it would be:

JSON.stringify(objWithFunction, function(key, val) {
  if (typeof val === 'function') {
    return val + ''; // implicitly `toString` it
  }
  return val;
});

There are some legitimate uses for what you're asking despite what people are posting here, however, it all depends on what you're going to be using this for. There may be a better way of going about whatever it is you're trying to do.

chjj
  • 14,322
  • 3
  • 32
  • 24
  • 12
    Is more clean if instead of doing an implicit _toString_ with `return val + '';`, you do an **explicit** _toString_, like : `return val.toString();` – chris-l Apr 23 '14 at 22:59
  • 2
    and when you do eval on the other side, you loose the function as you get a stringified function value to the key. – Neha Choudhary Jan 16 '15 at 07:12
  • 2
    Only pure _closed_ expressions should be serialised and sent over JSON. This is actually pretty powerful, one can send logic via RPC instead of just scalar values. The receiver can also type check the javascript prior to evaluating it, while also sandboxing it. Doing something interesting with this would blur the lines between code and data. – CMCDragonkai Feb 23 '17 at 12:44
  • How might one include the prototype in the serialization? – Eric Martindale Dec 15 '17 at 11:04
12

In fact, It's very easy to serealize / parse javascript object with methods.

Take a look at JSONfn plugin.

http://www.eslinstructor.net/jsonfn/

Hope this helps.

-Vadim

vadimk
  • 1,461
  • 15
  • 11
  • doesnt stringify getter setter, also when it returns type of object said to be some kind of Object, not the Type i had defined before... – Hassan Faghihi Dec 14 '15 at 08:00
8

Why exactly do you want to stringify the object? JSON doesn't understand functions (and it's not supposed to). If you want to pass around objects why not do it one of the following ways?

var x = {name: "FirstName", age: "19", load: function () {alert('hai')}, uniq: 0.5233059714082628};

function y(obj) {
    obj.load();
}

// works
y({name: "FirstName", age: "19", load: function () {alert('hai')}, uniq: 0.5233059714082628});

// "safer"
y(({name: "FirstName", age: "19", load: function () {alert('hai')}, uniq: 0.5233059714082628}));

// how it's supposed to be done
y(x);
David Titarenco
  • 32,662
  • 13
  • 66
  • 111
  • Thanks you for your answer! I want to add `y(x)` to an `onclick` event, so I must pass the whole object to `y()` and this doesn't seem to work. I think I'm missing something .. – Sorin Buturugeanu Jul 19 '11 at 22:56
  • the script is as follows: `elem.setAttribute('onchange', "file_onChange(" + data + ");");` where data is the object described above. And `file_onChange()` must receive the whole object .. – Sorin Buturugeanu Jul 19 '11 at 22:58
  • 1
    Finally figured it out: `element.onclick = function () { return function() { file_onReset(data) } } (); `. :) Thanks, your comment was the spark that I needed! – Sorin Buturugeanu Jul 20 '11 at 00:13
6

Not something I would ever do but it is worth mentioning that there are ways to stringify function (i.e. it is not impossible).

Take the following:

var func = function(){console.log('logged')};
var strFunc = func.toString();

//then
var parsedFunc = eval('('+strFunc+')');
parsedFunc();

//or simply
eval('('+strFunc+')()');
//or
eval('('+strFunc+')')();
//or
eval('var anotherFunc='+strFunc);
anotherFunc()

The above with an overridden toJSON() can achieve a shallow stringified function;

DISCLAIMER: look into this and other articles and make your own decision before using eval()

RossBille
  • 1,448
  • 1
  • 17
  • 26
0

Here's some code I have used in my previous projects, maybe useful?

  // Similar to JSON.stringify(), with three major differences:
  // (1) It also wrap functions
  // (2) Upon execution, it expose an object, nl, into the scope
  // (3) Results are minified by 'uglify-js'
  var bundle = function(obj) {
    var type = typeof obj;
    if(type === 'string') return '\'' + obj + '\'';
    if(type === 'boolean' || type === 'number') return obj;
    if(type === 'function') return obj.toString();
    var ret = [];
    for(var prop in obj) {
      ret.push(prop + ': ' + bundle(obj[prop]));
    }
    return '{' + ret.join(',') + '}';
  };
  var ret = 'var nl = ' + bundle(nl);
  ret = require('uglify-js').minify(ret, {fromString: true}).code;
  fs.writeFileSync('nl.js', ret);

One Caveat when using this is that if the function uses anything in external closure, it won't work, ie: ... obj: {..., key: (function() {...var a = 10;... return function() {... some code uses 'a'...})()}

streaver91
  • 864
  • 1
  • 8
  • 10
  • How can I make this function property stringify arrays? It is converting arrays into hash objects. – Rolf Jan 18 '16 at 17:43
  • @Rolf you can add this statement above the if lines `if(obj instanceof Array) return JSON.stringify(obj);` – buriedalive Jun 01 '21 at 12:08
0

You can override the toJSON() method, which serializes the function to another object. Here is an example which converts the function to a string:

Function.prototype.toJSON = Function.prototype.toString;
var o = {name: "FirstName",
age: "19",
load: function () {},
uniq: 0.5233059714082628};

console.log(JSON.stringify(o,null,4))

If you wanted to print, say, the property descriptors instead:

Function.prototype.toJSON = function() {
    return Object.getOwnPropertyDescriptors(this);
}

var o = {name: "FirstName",
age: "19",
load: function () {},
uniq: 0.5233059714082628};

console.log(JSON.stringify(o,null,4))
Nirvana
  • 405
  • 3
  • 15