Shallow copying in Javascript - what for?

36 views
Skip to first unread message

Inger Hohler

unread,
Mar 21, 2021, 9:56:50 AM3/21/21
to Khan Academy Javascript Technical Q&A
Normally when I make copies of objects (object literals) or arrays I want my copies to be deep copies, and I know several ways to make them so. If I have nesting I normally use the function I found here:
https://www.codementor.io/avijitgupta/deep-copying-in-js-7x6q8vh5d
- modified so I could use it on KA to:

var copyAwO= function(o) {
   var output, v, key;
   output = Array.isArray(o) ? [] : {};
   for (key in o) {
       v = o[key];
        output[key] = (typeof v === "object") ? copyAwO(v) : v;
        }
    return output;
};
For non-nested cases I tend to use Object.create.

However, shallow copies of complex datatypes are more easily made, and I have found a few references to situations where they could be an advantage.

The most commonly cited reason for using shallow copies, is that if the values on the objects aren't going to change, shallow copies are more lightweight solution. They take up less browser memory. One example of this explanation is the top voted 
https://stackoverflow.com/questions/3351018/when-is-a-shallow-copy-desirable-instead-of-a-deep-copy
"When the owned variables are immutable types, a shallow copy is sufficient. A deep copy is possible, but would only result in additional memory use."

I appreciate that 500 shallow copies take up less browser memory than 500 deep copies. What I don't understand is why it would be desirable to have 500 exact copies of the same object that won't change. When I create a function, that function can be re-used many times. With the right method I could easily display or use the information from the same object many times over on a page, so I would not need more than one copy of said object. 

Do any of you have or know of a practical example that might make it easier to understand?

There was another reason in the same thread that I'll play around with some more before I possibly conclude I don't understand what it means.

best,
Inger

Larry Serflaten

unread,
Mar 21, 2021, 11:37:05 AM3/21/21
to Khan Academy Javascript Technical Q&A
 famh... @ gmail.com wrote:
 
I appreciate that 500 shallow copies take up less browser memory than 500 deep copies. What I don't understand is why it would be desirable to have 500 exact copies of the same object that won't change. When I create a function, that function can be re-used many times. With the right method I could easily display or use the information from the same object many times over on a page, so I would not need more than one copy of said object. 

Do any of you have or know of a practical example that might make it easier to understand?


For an off the top of my head answer, suppose a web page has a list of purchase orders (whatever) that are in chronological order.  To show the list the web designer is using a library that has an HTML listbox element that accepts an array of objects (or database recordset) and lists the contents of that array showing each object and its property values on individual rows.  (Check out KA SQL reports).

Now suppose the list (or any parts of that list) can be shown in any order, multiple times on the same page, for example, ordered by company, ordered by price, etc. (top 10 companies, top 10 items)

To show the list a second time on the same page the web designer would add another listbox to the page and need to give it an array in the desired order.  So a shallow copy of the original array is made, sorted to the proper order, and passed to the second listbox while the original array and listbox are unchanged.

You could use the one list and include the JavaScript necessary to show two (or more) different ordered lists, but most programmers are a lazy sort!  If there is a package that will do most of the work (reliably, and well tested) they are going to go that route, rather than do the work themselves. 

That might be one place to use a shallow copy, anytime you want to reorder a list without disturbing the original.

HTH
LFS

Bob Lyon

unread,
Mar 21, 2021, 3:33:04 PM3/21/21
to Khan Academy Javascript Technical Q&A
Larry gives a very common scenario for using shallow copy, and any array's `slice` method will provide that copy.

I cannot give a common scenario for deep copy. I doubt that we can find a deep copy example in any of my programs, which helps to confirm my opinion/assertion that if you are using deep copy, then you are probably doing it (the program) wrong.

That said, I cannot resist the bait to pontificate about deep copy.  Here are various random observations:
  1. By going deep, the example function `copyAwO` fails to preserve the very special object `null`. Its deep copy is a non-null empty object.  The distinction between `null` and an empty object is that `null` is the only Javascript object that coerces to the false boolean.
  2. From an OOP point of view, deep copy is a deep memory failure because it promotes all of a source object's prototype properties to be full-fledged properties of the copy.  For example, a new PVector has five properties, but its deep copy has 25 properties.  (Refer to the objects' `hasOwnProperty` method.)
  3. From an OOP point of view, deep copy may be inadequate.  A constructor function may do more than just set property values.  That value-add will be missing in the deep copy.
  4. Cycles should be handled, e.g. where two objects reference each other.
  5. I like JSON `stringify` and `parse`.  Some other smart people have determined what is, and what isn't (important), with respect to copying.
/Bob

Inger Hohler

unread,
Mar 21, 2021, 8:34:56 PM3/21/21
to Khan Academy Javascript Technical Q&A

Those are interesting answers from both of you - Thanks.

Larry's answer is indeed a type of use I can imagine :-) I was vaguely thinking about data from quantitative chemical analysis, where the lab routinely takes parallel measurements, and where batch number and name of the technical staff will be entered together with that particular run of tests. If a value had been entered incorrectly it would be useful to have shallow copies so the correction did not have to be repeated everywhere the value was used, for instance when calculating the mean. Such data would also often be displayed in more ways than one - only in the old days when I was handling that kind of data I was counting myself lucky to have a spreadsheet and not just graph paper and colored ball pens. There was no quick way to correct the pen and paper version, in a world where Tippex was banned for quality assurance reasons.

As to Bob's answer, thanks for the reminder about array.slice. I could certainly use it more than I have.
1) is of course correct. I don't think I've  used null values yet, but a general function should also be able to take care of those.
2) Point taken. KA mainly teaches inheritance in the advanced section, so I've spent much more time playing with those and prototype chains than the smaller literals. Additionally, nearly all the objects I've created have had an x or an y, or PVector and have been animated. Making shallow copies of those haven't crossed my mind because animating a copy together with the original would mean one was drawn on top of the other.
However, since I occasionally go wild with loops and objects in arrays I know I need to learn to utilize more light weight solutions. 
3) I'll have to explore this.
4) Not sure what that means in this setting. Shallow copies do not reference each other - both copy and original reference a location which holds the value. I know that interacting objects of different prototypes or different object literals can become a tangled mess. To avoid it I try to ensure a 'look but don't touch' approach. I write methods that look up information on another object, but only changes itself. Is this what you mean by 'cycles should be handled..."? If I have to use (look up) an object in a method before that object has been declared I create a parameter and pass the object to the method. It is quite possible that I have totally misunderstood what you are saying.
5) I found several references to the JSON stringify and parse method of making a deep copy online, but didn't get it to work in a JS program on KA. Now I see that there is a thread about it in the Help center from 6 years ago, so I'll see if I can make use of some of the suggestions there.  The alternative is to do it in a web page, I think.

Inger Hohler

unread,
Mar 22, 2021, 3:36:19 AM3/22/21
to Khan Academy Javascript Technical Q&A
Looks like I inadvertently pressed a button for answering only myself. If this is a duplicate I'll try to remove it.

Those are interesting answers from both of you - Thanks.

Larry's answer is indeed a type of use I can imagine :-) I was vaguely thinking about data from quantitative chemical analysis, where the lab routinely takes parallel measurements, and where batch number and name of the technical staff will be entered together with that particular run of tests. If a value had been entered incorrectly it would be useful to have shallow copies so the correction did not have to be repeated everywhere the value was used, for instance when calculating the mean. Such data would also often be displayed in more ways than one - only in the old days when I was handling that kind of data I was counting myself lucky to have a spreadsheet and not just graph paper and colored ball pens. There was no quick way to correct the pen and paper version, in a world where Tippex was banned for quality assurance reasons.

As to Bob's answer, thanks for the reminder about array.slice. I could certainly use it more than I have.
1) is of course correct. I don't think I've  used null values yet, but a general function should also be able to take care of those.
2) Point taken. KA mainly teaches inheritance in the advanced section, so I've spent much more time playing with those and prototype chains than the smaller literals. Additionally, nearly all the objects I've created have had an x or an y, or PVector and have been animated. Making shallow copies of those haven't crossed my mind because animating a copy together with the original would mean one was drawn on top of the other.
However, since I occasionally go wild with loops and objects in arrays I know I need to learn to utilize more light weight solutions. 
3) I'll have to explore this.
4) Not sure what that means in this setting. Shallow copies do not reference each other - both copy and original reference a location which holds the value. I know that interacting objects of different prototypes or different object literals can become a tangled mess. To avoid it I try to ensure a 'look but don't touch' approach. I write methods that look up information on another object, but only changes itself. Is this what you mean by 'cycles should be handled..."? If I have to use (look up) an object in a method before that object has been declared I create a parameter and pass the object to the method. It is quite possible that I have totally misunderstood what you are saying.
5) I found several references to the JSON stringify and parse method of making a deep copy online, but didn't get it to work in a JS program on KA. Now I see that there is a thread about it in the Help center from 6 years ago, so I'll see if I can make use of some of the suggestions there.  The alternative is to do it in a web page, I think.

Bob Lyon

unread,
Mar 22, 2021, 7:01:01 PM3/22/21
to Khan Academy Javascript Technical Q&A
4. Try
var obj1 = {};
var obj2 = { buddy: obj1 };
obj1.buddy = obj2;
var obj3 = copyAwO(obj1);

5. To get hold of JSON at Khan Academy, start the program with:
var json = (function(w) { return this[w]; })("JSON"); // Khan Academy silliness
Then use the json.stringify and json.parse functions.

Larry Serflaten

unread,
Mar 22, 2021, 10:35:45 PM3/22/21
to Khan Academy Javascript Technical Q&A
 famh... @ gmail.com wrote:
 
4) Not sure what that means in this setting.

The copyAwO function does not handle circular references.  If node object A has a 'neighbor' property pointing to node object B, and node object B has a 'neighbor' property pointing to node object A, they each reference each other.  That is a 'circular reference'.  If you try to do a deep copy on either object, the copy function would get stuck in a recursive loop trying to resolve the 'neighbor' property of every object.

I just saw Bob's reply with the copy example, so I though I would try to explain it in words....

LFS  

Inger Hohler

unread,
Mar 23, 2021, 9:01:17 AM3/23/21
to Khan Academy Javascript Technical Q&A
Again, both answers are very helpful.
When I read things online there is lots of information about techniques, and much less about when to use it. When there is information about use, there is usually disagreement. My own book about Javascript does not go into the whys of deep and shallow copies, nor have I found it in Eloquent Javascript, nor in my Python book. My husband's old C++ basically said 'don't use shallow copying'.
No matter if the 'don't use shallow copying' from the C++ book was an outdated opinion, or specific to C++, it's great to have examples and tips that are specifically related to Javascript.
Reply all
Reply to author
Forward
0 new messages