9

Vanilla JS only please

That is, its the output should be an object that only contains data, and ignores the original's methods/prototype. Complex data structures that inherit from the default Object, like Array, can be copied in a shallow manner, as references. The way I do it now is:

function shallowCopyObjectData(obj) {
  output = {};
  for (var i in item) {
    output[i] = obj[i];
  }
  return output;
};

The other way I've seen is:

function shallowCopyObjectData(obj) {
  return JSON.parse(JSON.stringify(obj));
};

What is the most performant way to do it?

I've made a running jsPerf to compare speeds. If you come up with a solution, please feel free to fork and add: http://jsperf.com/shallow-object-data-copy

Edit @Barmar: I know a similar question has already been posted, but it asked about the fastest way to clone an object, which implied a deep copy that keep the constructor, prototype, etc. This question asks about the fastest way to copy just the data in the top level

Community
  • 1
  • 1
AlexZ
  • 11,515
  • 3
  • 28
  • 42
  • 1
    @Barmar, I don't think its fair to mark this as a duplicate. The other question asked about the fastest way to clone an object, which implied a deep copy that keep the constructor, prototype, etc. This question asks about the fastest way to copy just the data in the top level. I feel like those are two different things... – AlexZ Jan 03 '15 at 02:32
  • `JSON.parse(JSON.stringify())` is much, much, faster than traversing each node of an object. – TheIronDeveloper Jan 03 '15 at 02:41
  • Really? Even if I have non-compliant data-types (like functions) as object properties? – AlexZ Jan 03 '15 at 02:42
  • 1
    Anecdotal example: I have a webapp that traverses and caches data on 53,000 objects. It was giving my node app a huge delay hit when it ran, taking up to 75829ms to finish loading everything. I swapped it with a parse/stringify, and it sped up to 718ms. – TheIronDeveloper Jan 03 '15 at 02:45
  • 3
    Do you actually *need* to shallow-copy the entire object? Or is there some predictable set of property names you can use? – user229044 Jan 03 '15 at 02:47
  • 1
    I want to add, that functions don't seem to properly get saved with parse/stringify. :| – TheIronDeveloper Jan 03 '15 at 02:49
  • @meagar: yes, the property list is completely unpredictable. – AlexZ Jan 03 '15 at 02:50
  • @TheIronDeveloper: hmmmm, just added a jsPerf I made for my two versions, and the for loop seems a bit faster. It doesn't try to mess with and ignore functions in that example though. – AlexZ Jan 03 '15 at 02:52
  • @TheIronDeveloper They definitely don't. JSON is not JavaScript, it doesn't have functions. You cannot JSON-encode functions. – user229044 Jan 03 '15 at 02:53
  • Perhaps `JSON.parse(JSON.stringify())` is only faster on an array of objects, not one. – TheIronDeveloper Jan 03 '15 at 02:58
  • 2
    If your objects are known to have the same structure (tabular data) you could generate a cloner. And gain a huge performance boost since your objects will share the same hidden class. http://jsperf.com/shallow-object-data-copy/3 – Yury Tarabanko Jan 03 '15 at 03:09
  • Wow, that is a huge boost. Unfortunately the contents and structure of the object are completely arbitrary. – AlexZ Jan 03 '15 at 03:23
  • @TheIronDeveloper DO NOT use `JSON.stringify`, what if the object has a method? – Leo Jan 03 '15 at 03:35
  • Performance is always a combination of many factors. Optimize data structure may bring you much benefit as well. Don't just look at one point (and sometimes, unfortunately, might not be the real bottle neck). – Leo Jan 03 '15 at 03:44
  • @Leo yea we discussed that earlier. For my particular case, my objects did not contain functions, so `JSON.stringify` met my needs and was much more performant than parsing through 53,000 objects. – TheIronDeveloper Jan 03 '15 at 03:51
  • 1
    These don't even do the same thing! `JSON.parse(JSON.stringify())` will do a _deep_ copy, not a shallow one. – Dan Mar 10 '16 at 13:18
  • I tried this with ES6 object spread, and wow! I cannot believe object spread is **this** slow. https://jsperf.com/es6-shallow-copy-object – Dallas Oct 13 '17 at 07:11
  • To be entirely fair, it seems like Object.assign (and ES6 spread, which I believe is just syntactic sugar on Object.assign) does a little more under the hood than the for loop, so its not entirely surprising that it runs a bit slower: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill – AlexZ Oct 13 '17 at 18:03
  • You're using **shallow copy** in a very non-standard way. What you want is not called **shallow copy**. I'm not sure what you want has a name but what I'm certain is that it's not shallow copy. Shallow copy is copying pointers/references instead of values – slebetman Jun 04 '22 at 11:26

5 Answers5

5

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object. Properties in the target object will be overwritten by properties in the sources if they have the same key.

var obj = { a: 1, b: 2, };
var new_obj = Object.assign({}, obj);
console.log(new_obj); //{ a: 1, b: 2, }
console.log(new_obj == obj); //false
nircraft
  • 8,242
  • 5
  • 30
  • 46
Maris
  • 59
  • 1
  • 3
  • 2
    Hi - can you please add a little bit more context/an explanation for your answer please? :) – party-ring Aug 20 '19 at 14:46
  • 1
    eslint recommends using the spread operator `var new_obj = {...obj}` it's available in all modern web browsers: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax – Charles L. Feb 09 '21 at 21:41
0

Nowadays you can use spread just like;

var o = {a:1,b:2,c:3},
    r = {...o}; // shallow clone

r.a = 11;       // r's "a" property is set to 11
console.log(r); // check
console.log(o); // r is not a reference to a
Dharman
  • 30,962
  • 25
  • 85
  • 135
Redu
  • 25,060
  • 6
  • 56
  • 76
-1

You can use Object.assign to fast clone object. The syntax is Object. assign(originObject, extendObject). It will return the object that have property of originObject and extendObject.

For example I have the code here:

var originObject = {value: 1};
var extendObject = {key: 2};
var shadowObject = {};

// Now I want clone originObject to shadow object
shadowObject = Object. assign(originObject, {});
// shadowObject = {value: 1}

// If you want clone originObject and extendObject to one object
shadowObject = Object. assign(originObject, shadowObject);
// shadowObject = {value: 1, key: 2}
ross
  • 2,684
  • 2
  • 13
  • 22
Trần Công
  • 288
  • 1
  • 5
-2

Note that if you know the property names in advance, you can get multiple orders of magnitude of improvement by writing them out in an object literal:

function cloneObject(obj) = {
    return {
        "property1": obj.property1,
        "property2": obj.property2,
        //Etc.
    }
}

Anything that involves a loop is going to be slow, so if there's an easy way to avoid the loop, do that instead.

Here are the results of some tests I ran in the most recent (as of April 2023) version of Chrome:

  • structuredClone(obj): 200 copies per ms.
  • JSON.parse(JSON.stringify(obj)): 400 copies per ms.
  • Getting the object's properties with Object.keys() and looping over them with a while loop, removing the first element from the list on each iteration: 2000 copies per ms.
  • Getting the object's properties with Object.keys and looping over them with forEach(): 2100 copies per ms.
  • Getting the object's properties with Object.keys() and looping over them with a traditional for loop: 2250 copies per ms.
  • Looping over a predefined list of properties with for... of: 2300 copies per ms.
  • {...obj}: 2500 copies per ms.
  • Object.assign({}, obj): 3100 copies per ms.
  • Object literal with predefined properties: 1 million copies per ms.
Isaac King
  • 283
  • 3
  • 13
  • Please add the actual tests you ran. The times depend on the number of properties on the object. – trincot Apr 30 '23 at 21:46
  • They were all run with the same object properties. (13 in this case.) I'm not claiming that other people will get the exact same times, it's just to illustrate the point that some things are much faster than others. – Isaac King Apr 30 '23 at 21:55
  • 2
    I cannot reproduce these timings. I did a test that compares object spread with the `Object.keys().forEach` solution, and the latter is 10 times slower. Please prove your claims with a script that can reproduce those timings (approximately). – trincot Apr 30 '23 at 21:58
  • You're right, I completely misunderstood how forEach() works, and was also looking at the wrong field in the Chrome profiler so was seeing incorrect results. Oops. I've edited my answer to hopefully be correct now; thank you for catching that. – Isaac King Apr 30 '23 at 22:30
-4

I think your asking about Deep Cloning (Copy). A shallow copy is as simple as assigning the original object to the new variable.

var originalObj = { someKey: 1 };
var copyObj = originalObj; 
  • I think there is a big difference. Assigning reference doesn't create a new object, shallow copy does. In the example you gave, `copyObj === originalObj` will give `true`, but it should be `false` for shallow copy. – Gan Quan May 10 '18 at 05:39
  • This is just plain wrong. You're mixing identity and value. You can assign a value to a new identity (variable name) without copying the underlying value. OP asks about shallow copying. This can only happen on the actual value by definition of the operation. `Object.assign` or JS spread operator aka `{...obj}` are the way to go. – bash0r Sep 22 '19 at 00:49