js object copy

Keywords: Javascript JSON Attribute REST

Object Introduction

js contains values of two different data types: base type values and reference type values. Basic type values refer to simple data segments, while reference type values refer to objects that may be composed of multiple values.

JS objects are all reference types, and objects are instances of a particular reference type. Object is the basic block of js. Objects are collections of attributes, and attributes are key-value pairs. Most of the reference types seen in JS are instances of Object type, and Object is also the most frequently used type in js.

Assignment by reference

let obj1 = {
    name : 'jack',
    number: 10
}
let obj2 = obj1 // Assigning a reference to obj1 to obj2 points obj1 and obj2 to the same memory space
obj2.name = 'tom'
console.log(obj1.name) // tom

But when you copy the value of a reference type from one variable to another, you also copy the value stored in the variable object and place it in the space allocated by the new variable. The difference is that a copy of this value is actually a pointer to an object stored in the heap. After the replication operation, the two variables will actually refer to the same object. So changing one variable changes the other.

How to copy objects

1. The original method of copying objects is to iterate over the original objects and then copy each attribute one by one.

let objCopy = {}; // objCopy will store a copy of mainObj

function copy(mainObj) {
  let key;
 
  for (key in mainObj) {
    objCopy[key] = mainObj[key]; // Copy each attribute to an objCopy object
  }
  return objCopy;
}
 
const mainObj = {
  a: 2,
  b: 5,
  c: {
    x: 7,
    y: 4,
  },
}
 
console.log(copy(mainObj));
objCopy.c.x = 10086
console.log("mainObj.c.x", mainObj.c.x)  // 10086

The above code only copies the enumerable properties of mainObj.

If an attribute in the original object is itself an object, the object will be shared between the copy and the original object so that their respective attributes point to the same object.

2. Shallow copy

Shallow copy is that objects share a memory address, and the changes of objects affect each other. For example, a common assignment reference is a shallow copy:

let obj1 = {
    name : 'jack',
    number: 10
}
let obj2 = obj1 // Assigning a reference to obj1 to obj2 points obj1 and obj2 to the same memory space
obj2.name = 'tom'
console.log(obj1.name) // tom
Object.assign()
let obj = {
  a: 1,
  b: {
    c: 2,
  },
}
let newObj = Object.assign({}, obj);
console.log(newObj); // { a: 1, b: { c: 2} }
 
obj.a = 10;
console.log(obj); // { a: 10, b: { c: 2} }
console.log(newObj); // { a: 1, b: { c: 2} }
 
newObj.a = 20;
console.log(obj); // { a: 10, b: { c: 2} }
console.log(newObj); // { a: 20, b: { c: 2} }
 
newObj.b.c = 30;
console.log(obj); // { a: 10, b: { c: 30} }
console.log(newObj); // { a: 20, b: { c: 30} }
 
// Note: newObj.b.c = 30; why?

bject.assign is just a shallow copy. Both newObj.b and obj.b refer to the same object, instead of being copied separately, they copy the reference to that object. Any change to an object's attributes applies to all references that use the object.

Note: Attributes and non-enumerable attributes on the prototype chain cannot be copied.

let someObj = {
  a: 2,
}
 
let obj = Object.create(someObj, { 
  b: {
    value: 2,  
  },
  c: {
    value: 3,
    enumerable: true,  
  },
});
 
let objCopy = Object.assign({}, obj);
console.log(objCopy); // { c: 3 }

Some Obj is a prototype chain of obj, so it will not be replicated.
Attribute b is not an enumerable attribute.
Attribute c has an enumerable attribute descriptor, so it can be enumerated. That's why it's copied.

Use the expansion operator (... )

ES6 already has rest elements for array deconstruction assignments, and operators for implementing array literal expansion.

const array = [
"a", "c", "d", { four: 4 },
];
const newArray = [...array];
console.log(newArray);
// Result 
// ["a", "c", "d", { four: 4 }]

Only for shallow copies

3. Deep copy

Simply understand that deep copy is to put objects in a new memory, and the changes of the two objects will not affect each other.

JSON.parse() and JSON.stringify()
srcObj = {'name': 'bright', grade: {'chi': '50', 'eng': '50'} };
// copyObj2 = Object.assign({}, srcObj);
copyObj2 = JSON.parse(JSON.stringify(srcObj));
copyObj2.name = 'red';
copyObj2.grade.chi = '60';
console.log('JSON srcObj', srcObj); // {name:'ming', grade: {chi:'50', eng:'50'}}

You can see that changing copyObj2 does not change the original object, and implements the basic deep copy.
But there is a problem with JSON.parse() and JSON.stringify().
JSON.parse() and JSON.stringify() can correctly handle only Number, String, Array and other data structures that can be represented by json, so the type of function that can not be represented by JSON will not be correctly handled. such as

srcObj = {'name': 'bright', grade: {'chi': '50', 'eng': '50'},
    'hello': function() {console.log('hello')}};
// copyObj2 = Object.assign({}, srcObj);
copyObj2 = JSON.parse(JSON.stringify(srcObj));
copyObj2.name = 'red';
copyObj2.grade.chi = '60';
console.log('JSON srcObj', copyObj2); //{name:'red', grade: {chi:'60', eng:'50'}}}

As you can see, function s are lost after conversion, so JSON.parse() and JSON.stringify() still need to be used with caution.

Deep and shallow copies of arrays

Finally, in addition to a few deep and shallow copies of arrays, shallow copies of arrays, like objects, also change one of them. For example:

let srcArr = [1, 2, 3];
let copyArr = srcArr;
copyArr[0] = '0';
console.log('srcArr', srcArr); // ['0', 2, 3]

But the deep copy method of arrays is relatively simple, which can be understood as methods that change the original array in the array method, such as

  • concat
  • slice
  • Array of es6.

Blog Reference

Posted by Thunderfunk on Mon, 05 Aug 2019 03:08:34 -0700