13

In my component i have declarated some data like this:

data() {
    return {
        defaultValue: {json object with some structure},
        activeValue: {}
        ...

And in component methods a make copy this value:

this.activeValue = this.defaultValue

But problem is, after change this.activeValue value a have changes in this.defaultValue too.

If i use Object.freeze(this.defaultValue) and trying change this.activeValue i have get error - object is not writable.

How i can make copy of data but without reference?

Owl
  • 6,337
  • 3
  • 16
  • 30
Lajdák Marek
  • 2,969
  • 8
  • 29
  • 58

7 Answers7

19

If you have simple object, quickest and easiest way is to just use JSON.parse and JSON.stringify;

const obj = {};

const objNoReference = JSON.parse(JSON.stringify(obj));
Owl
  • 6,337
  • 3
  • 16
  • 30
7
this.activeValue = { ...this.defaultValue }

Using an ES6 spread operator will help you to do a copy if you do not have a nested object. If you equate using equal = sign, it will not create a new object, it will just create a variable with the reference to the current object (like a shallow copy).

To do a complete deep copy, even it is nested object, go for this:

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

as suggested by Owl.

Click to read more for better understanding of the concept

ANIL PATEL
  • 673
  • 7
  • 16
  • 2
    There's a confusion with terms. `{ ...this.defaultValue }` isn't destructuring but spread syntax. It creates a shallow and not deep copy - unless you do this explicitly with `{ ...this.defaultValue, foo: { ...this.defaultValue.foo } }`. `=` copies by reference and doesn't make a shallow copy. – Estus Flask Jul 11 '20 at 12:26
  • 2
    JSON method removes data types like `Map` & `Regex` and also stringfy `Date` Very possible to cause bugs – Zortext Oct 15 '21 at 09:42
  • @Zortext true. Thanks for pointing it out. If the value of any of the keys in the object (in nested object also) is Map, Date, Function, Regex, Set etc it won't work exact clone of the object. – ANIL PATEL May 16 '22 at 10:31
5

A nicer way rather than using JSON.parse, JSON.stringify is:

this.activeValue = {...this.defaultValue}

but this is not natively supported by some browser (IE), unless used with a transpiler (babel)

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

Update

Considering your originial question is about a way in Vue, there is also a native method in vue:

this.activeValue = Vue.util.extend({}, this.defaultValue)

as for this answer.

Hope this helps!

Raffobaffo
  • 2,806
  • 9
  • 22
1

Objects are assigned and copied by reference.

All operations via copied references (like adding/removing properties) are performed on the same single object.

To make a “real copy” (a clone) we can use Object.assign for the so-called “shallow copy” (nested objects are copied by reference).

For “deep cloning” use _.cloneDeep(obj) from loadash library.

1

JSON stringify&parse method have some issues like converting date objects to strings. It also cannot handle special data types like Map,Set,function etc... This is prone to future bugs.

I use the following method to deep copy an object.

REMEMBER! this is not a complete application of cloning. There are more data types to handle like Blob, RegExp etc...

  const deepClone = (inObject) => {
            let outObject, value, key

            if (typeof inObject !== "object" || inObject === null) 
                return inObject
            
            if (inObject instanceof Map) {
                outObject = new Map(inObject);
                for ([key, value] of outObject)
                    outObject.set(key, deepClone(value))
            } else if (inObject instanceof Set) {
                outObject = new Set();
                for (value of inObject)
                    outObject.add(deepClone(value))
            } else if (inObject instanceof Date) {
                outObject = new Date(+inObject)
            } else {
                outObject = Array.isArray(inObject) ? [] : {}
                for (key in inObject) {
                    value = inObject[key]
                    outObject[key] = deepClone(value)
                }
            }
            return outObject
        }
Zortext
  • 566
  • 8
  • 21
0

You can use 'JSON.parse and stringify' or using some clone function in the libs like lodash (underscore, ramda...)

rian
  • 370
  • 1
  • 6
-1

Also a simple solution is to store defaultValue: {json object with some structure} with JSON.stringify(defaultValue) in a string variable:

var x = JSON.stringify(this.defaultValue);

If you need it as JSON object again you can get it with JSON.parse():

var newObject = JSON.parse(x);

The object reference is also broken doing it this way, x will stay unchanged if the content of the object defaultValue is altered.