1

JSON.stringify(value) will convert a JavaScript value to JSON string. JSON.parse(value) will convert a valid JSON string into a JavaScript value (object, array or other JSON-available primitive).

Why would you take a JavaScript value and move it through JSON ? Means take a value then stringify it and then parse it back. It serves no purpose to my knowledge and just wastes machine resources.

I'm asking this is because I've come across this function:

function ser(value) {
    return value == null ? null : JSON.parse(JSON.stringify(value))
}

it's found in crow-tech.js in sandbox of Chapter 11 of book Eloquent JavaScript by Marijn Heverbeke and I'm wondering why would he wanna do that!

Danish ALI
  • 65
  • 1
  • 9
  • 2
    No point in stringifying then parsing unless you're trying to deep clone it, but you can't send a *plain Javascript object* over the network - so, stringify it on one end, send the JSON string, then parse it on the other end (for example, if both ends use JS) – CertainPerformance Nov 02 '18 at 04:36
  • one of the reasons : https://stackoverflow.com/q/53112718/2630817 – Just code Nov 02 '18 at 04:41
  • If it helps https://stackoverflow.com/questions/20662319/javascript-deep-copy-using-json – RedPandaz Nov 02 '18 at 04:45
  • Thanks! I got this: You could clone (deep copy) value by this method but with (function/type info) removed. (also not to use this for big objects as it would create performance issues - but could be used as fast shorthand for little objects deep-copy with only JSON-able data) – Danish ALI Nov 02 '18 at 06:29
  • Possible duplicate of [javascript deep copy using JSON](https://stackoverflow.com/questions/20662319/javascript-deep-copy-using-json) – yivi Nov 02 '18 at 07:40

2 Answers2

4

This is a cheap way to deep clone an object in JavaScript. Consider the following:

function a() {
  const obj = {name: "fred"};
  
  b(obj);
  
  console.log(obj);
}


function b(obj) {
  obj.age = 42;
}

a();

The function a passes an object to b and when it gets modified. Sometimes that is what you want, other times, you want to preserve the original object from modifications, so you have to clone it. JavaScript doesn't have any facility to do that but JSON.stringify -> JSON.parse will create a new object for you so it be used for cloning:

function a() {
  const obj = {name: "fred"};
  
  b(JSON.parse(JSON.stringify(obj)));
  
  console.log(obj);
}

function b(obj) {
  obj.age = 42;
}

a();

This is a good illustration of what can get wrong, but in the real world, it's not always so simple. The above can also be done through a shallow clone of the object

function a(){
  const obj = {name: "fred"};
  
  b(Object.assign({}, obj)); //shallow clone
  
  console.log(obj);
}

function b(obj) {
  obj.age = 42;
}

a();

However, that will fail in the following scenario:

const obj = {
  name: "fred",
  child: { name: "pebbles" }
};

const objClone = Object.assign({}, obj)

objClone.age = 42;
objClone.child.age = 2;

console.log('the "cloned" object was modified', objClone);

console.log("so was the original nested object", obj);

This is because the nested object child was not cloned, so we modified the top-level clone of the parent but then modified the original child. Here is when deep cloning is useful:

const obj = {
  name: "fred",
  child: { name: "pebbles" }
};

const objClone = JSON.parse(JSON.stringify(obj));

objClone.age = 42;
objClone.child.age = 2;

console.log('the cloned object was modified', objClone);

console.log("none of the original was", obj);

This doesn't solve all problems. JSON.stringify and then JSON.parse only works with very simple objects anywhere, it will not copy prototypes or functions. Some examples:

const obj = { name: "Alice" };
const proto = { age: 42 };

Object.setPrototypeOf(obj, proto);

console.log("assembled object", obj);

const clone = JSON.parse(JSON.stringify(obj));

console.log("cloned object", clone);

const objWithFunction = { name: "Bob", getAge: function() { return 42; } };

console.log("object with function assigned to property", objWithFunction);
console.log("getAge from original", objWithFunction.getAge());

const cloneWithFunction = JSON.parse(JSON.stringify(objWithFunction));

console.log("cloned object with function assigned to property", cloneWithFunction);
console.log("getAge from clone", cloneWithFunction.getAge());

function Person(name, age) {
  this.name = name;
  this.age = age;
}

const p = new Person("Carol", 42);

console.log("what is p:", p);
console.log("is p a Person?", p instanceof Person);

const clone = JSON.parse(JSON.stringify(p));
console.log("what is clone:", clone);
console.log("is clone a Person?", clone instanceof Person);
console.log("the clone is actually a plain object:", Object.getPrototypeOf(clone) == Object.prototype);
VLAZ
  • 26,331
  • 9
  • 49
  • 67
  • Got it all also from the direct comments (led to two other stackoverflow questions) I know that you also lose type information when you through an object from JSON – Danish ALI Nov 02 '18 at 06:46
  • @DanishALI yes, you do. I just added another example at the end to show another failure point. Still, it could be useful if you have plain objects (including arrays) nested into plain objects. In that case, it's definitely easier to `stringify` -> `parse` than to write a custom cloning utility. I'll add an example to demonstrate where the strength of this lies. – VLAZ Nov 02 '18 at 06:49
  • @DanishALI I added an example when you might want a deep clone. As long as your object is simple, then it's a reliable and cheap way to do the cloning. If you have anything complex, it becomes an issue. – VLAZ Nov 02 '18 at 07:03
2
JSON.parse(JSON.stringify(value))

This is mostly used to clone an object. You cannot directly copy a object to another variable because objects are copied by reference.

let user = {name : "John"}

let newUser = user; // newUser and user refer to same object (ie same memory location.) . 

If you modify user then newUser will also get modified. This is because objects are copied via reference not by value.

So to do a object copy we use the method you specified.

Example

let value = {"name": "John"}; // this is a object.

JSON.stringify(value) // were are changing the type object to string

Strings can be copied by value.

Now are we need to again convert string to Object, for that we use

let newValue = JSON.parse(JSON.stringify(value)); 

This returns a object - a clone of value

Now value and newValue are two different objects.

kiranvj
  • 32,342
  • 7
  • 71
  • 76