5

In one repo I saw a line.

var foo = JSON.parse(JSON.stringify(foo));

I think this is trying to strip any methods off the object. I can't really see it doing anything else. Is there a more efficient way to attempt this? Does node optimize this?

MKaras
  • 2,063
  • 2
  • 20
  • 35

2 Answers2

6

In the code context you have now disclosed, this technique is being used to make a copy of an object passed to a function so that modifications to that object will not modify the original. Here's the context from your link:

// route reply/error
this.connection.on('message', function(msg) {
   var msg = JSON.parse(JSON.stringify(msg));
   var handler;
   if (msg.type == constants.messageType.methodReturn || msg.type == constants.messageType.error) {
       handler = self.cookies[msg.replySerial];
       if (msg.type == constants.messageType.methodReturn && msg.body)
          msg.body.unshift(null); // first argument - no errors, null
       if (handler) {
          delete self.cookies[msg.replySerial];
          var props = {
             connection: self.connection,
             bus: self,
             message: msg,
             signature: msg.signature
          };
          if (msg.type == constants.messageType.methodReturn)
             handler.apply(props, msg.body); // body as array of arguments
          else
             handler.call(props, msg.body);  // body as first argument
       }

Note: the line in this clip that contains msg.body.unshift(null). That would be modifying the original object if this copy was not made.

Also, note that redeclaring var msg is not actually defining a new variable. Since msg is already defined in this scope as the function argument, it is not redeclared by the var msg (this is technically a mistake in the code to use the var).


The usual reason for using this type of code is to clone an object (make a deep copy of the object where all properties including embedded objects and arrays are copied).

var obj = {
   list: [1,2,3],
   items: [{language: "English", greeting: "hello"}, 
           {language: "Spanish", greeting: "hola"}, 
           {language: "French", greeting: "bonjour"}]
}

// make a completely independent copy of obj
var copy = JSON.parse(JSON.stringify(obj));

copy.items[0].greeting = "Yo";

console.log(obj.items[0].greeting);    // "hello"
console.log(copy.items[0].greeting);   // "Yo"

Note: this only works as a full copy with objects that are a plain Object type and do not have custom properties that are functions. And, because JSON.stringify() does not support circular references or self references, you can't have any of those. And, if you have multiple references to the same object, each reference will be copied to a new separate object. And, the combination of JSON.stringify() and JSON.parse() don't support objects other than a plain Object such as RegExp, Date or any of your own custom objects (they turn them into plain Objects). So, there are some limitations of this procedure, but it works quite simply for the majority of cases.


Per Matt (in comments), a custom function to create a clone of an object that does support circular references and does support some types of custom objects can be seen here.


In case anyone reading this doesn't realize, assigning an object to another variable does not make a copy in Javascript. The assignment in Javascript is like setting a pointer reference to the same object. Each variable then points to the same underlying object so modifying the object through either variable ends up modifying the same object in both cases like this:

var obj = {
   list: [1,2,3],
   items: [{language: "English", greeting: "hello"}, 
           {language: "Spanish", greeting: "hola"}, 
           {language: "French", greeting: "bonjour"}]
}

var copy = obj;

// modify copy 
copy.items[0].greeting = "Yo";

// both obj and copy refer to the exact same object
console.log(obj.items[0].greeting);    // "Yo"
console.log(copy.items[0].greeting);   // "Yo"

Thus, the occasional need to make an actual deep copy of an object.

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Also worth to note that this doesn't work with circular references and doesn't preserve multiple references to the same object (i.e. `[a, a]` would become `[a, b]`). – Felix Kling Mar 20 '15 at 01:33
  • 2
    @FelixKling - added your additional caveats to the answer. – jfriend00 Mar 20 '15 at 01:36
  • 1
    For those cases where you do need a deep clone of the object, see this answer: http://stackoverflow.com/a/13333781/560114 – Matt Browne Mar 20 '15 at 01:48
  • 1
    @MattBrowne - The method in the OP's question already does a deep clone. The method you linked to supports more features such as circular references, duplicate references and works for some custom object types (though it's quite possible to have object types that won't work that either - particularly if the constructor links up with other objects). – jfriend00 Mar 20 '15 at 01:53
  • @MattBrowne - I added a link to my answer to the clone function you referenced. – jfriend00 Mar 20 '15 at 02:47
  • `JSON.stringify` also turns regexps into empty objects `{}`. –  Mar 20 '15 at 03:07
  • @jfriend00 - just wanted to mention the code is putting the result in the same variable as the source. Why would there be a deep copy to itself? – MKaras Mar 23 '15 at 13:57
  • 1
    @MKaras - you will have to show the code context in which this is used to allow real answers as to why this is being done rather than guesses. Right now you have an academic guessing contest rather than an actual code analysis. – jfriend00 Mar 23 '15 at 15:22
  • @jfriend00 - the code in question is here https://github.com/sidorares/node-dbus/blob/master/lib/bus.js#L98 – MKaras Mar 23 '15 at 20:21
  • @MKaras - that specific reference is making a copy of the object that was passed into the function because later in the function the object is modified (`msg.body.unshift(null)`) so making a copy keeps the original from being changed by executing the function. – jfriend00 Mar 23 '15 at 20:59
  • @MKaras - I added an explanation about your specific code reference to the beginning of my answer. – jfriend00 Mar 23 '15 at 21:17
  • @MKaras - I'm wondering if this has successfully answered your question or if you still have more you need to understand. If this has answered your question, then please indicate so by checking the green checkmark to the left of the answer. If not, then please explain what else you need help understanding. – jfriend00 Apr 01 '15 at 19:09
0

If removing methods from the prototype is what you want, consider creating a new object and transferring over all of the old object's properties.

If stripping out properties that are a function is what you want, loop through the object and check if the property is a function:

for(key in ob)
{
    if(typeof ob[key] === 'function')
    {
        delete ob[key];
    }
}

Or perhaps what you hope to accomplish is the combination of them both.

flagoworld
  • 3,196
  • 2
  • 20
  • 16