Home > Enterprise >  Is it possible to stringify an array object with function saved in it in JavaScript?
Is it possible to stringify an array object with function saved in it in JavaScript?

Time:04-06

I have a code that generates how many users I wanted to create in an array.

let x;

const collection = {
  count: 2,
  array: [],
  create: function(ind) {
    this.array.push({
      name: "user "   ind,
      alert: function() {
        alert(this.name);
      }
    });
  }
};

for(let i = 0; i < collection.count; i  ) {
    collection.create(i);
};

When I tried to convert it to string,

x = JSON.stringify(collection.array);
console.log(x);

It just shows this. It doesn't save the function inside the object. Which means, I can't call the user to broadcast the user name itself.

'[{"name":"user0"},{"name":"user1"}]'

Is it even possible to convert this array with functions in it to string with just JavaScript?

CodePudding user response:

Taking into account two comments of mine ...

Instead of transporting redundant functional overhead like suggested by an answer via e.g.

'[{ "name": "user 1", "alertStr": "function() { console.log(this.name); }" }]'

per object, the receiving side should parse the plain object data and add the functional part right after via e.g map ...

function logName () {
  console.log(this.name);
}
const itemList = JSON
  .parse(data)
  .map(item => ({ ...item, logName }));

And how about deserializing via JSON.parse then? The result is an array with two objects where each object features an alertStr property with the redundant string value of 'function() { console.log(this.name); }'. How does this help the OP without having to use an eval based approach (either directly via eval or by parsing and new Function( ... ))? In addition, I consider Function.prototype.toString not a reliable method for the suggested purpose of saving a function's implementation.

.., I want to draw the OP's attention to the replacer parameter/function of JSON.stringify and the reviver parameter/function of JSON.parse.

The next provided example code has a more educational purpose, and any production code should try to avoid this solution by navigating around and/or solving the OP's real problem.

//     slightly improved OP example code    

function logName() {
  console.log(this.name);
}
const collection = {
  count: 2,
  array: [],
  create: function(idx) {
    this.array.push({
      name: `user ${ idx }`,
      logName,
    });
  },
};

for (let i = 0; i < collection.count; i  ) {
  collection.create(i);
};
console.log({
  collectionArray: collection.array,
});
collection.array[0].logName();
collection.array[1].logName();

console.log({
  plainlyStringified: JSON.stringify(collection.array),
});


//     serializing with a map/index based approach for function implementations    

const functionImplementationIndex = {
  logName: ['console.log(this.name);'],
  //total: ['a', 'b', 'return a   b;'],
}

function serializeFunction(key, value) {
  if (typeof value === 'function') {
    value = {
      isFunction: true,
      newFuncArgs: functionImplementationIndex[key] ?? [],
    };
  }
  return value;
}
const serializedArrayData =
  JSON.stringify(collection.array, serializeFunction);

console.log({ serializedArrayData });


//     deserializing/parsing again    

function deserializeFunction(key, value) {
  if (
    value.hasOwnProperty('isFunction')
    && (value.isFunction === true)
  ) {
    value = new Function(...(value.newFuncArgs ?? []));
  }
  return value;
}
const deserializedArray =
  JSON.parse(serializedArrayData, deserializeFunction);

console.log({ deserializedArray });
console.log({
  collectionArray: collection.array,
});
deserializedArray[0].logName();
deserializedArray[1].logName();
.as-console-wrapper { min-height: 100%!important; top: 0; }

CodePudding user response:

Not really. But you can do a some trick. You can stringify function with .toString() method

For example

fn = () => 'hello world';
fn.toString();  // "() => 'hello world'"

So you can add this "hack" to array:

const collection = {
  count: 2,
  array: [],
  create: function(ind) {
    const name = "user "   ind;
    const alert = function() { console.log(this.name); };
    const alertStr = alert.toString();
    this.array.push({ name, alert, alertStr });
  }
};

collection.create(1);
collection.create(2);

collection.array[0].alert()
collection.array[1].alert()

console.log(JSON.stringify(collection.array));
.as-console-wrapper { max-height: 100% !important; top: 0; }

  • Related