8

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
  • 10,657
  • 3
  • 25
  • 39
  • 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

4 Answers4

6

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
  • 7,430
  • 5
  • 28
  • 44
Maris
  • 61
  • 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
1

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
  • 26,923
  • 21
  • 73
  • 125
Redu
  • 22,595
  • 5
  • 50
  • 67
0

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,590
  • 2
  • 12
  • 22
Trần Công
  • 270
  • 1
  • 5
-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