Shallow and deep copy discovery JSON stringify

Shallow copy and deep copy JSON stringify

Basic type

There are seven basic data types: Number, String, Boolean, Null, Undefined, Symbol (ES6) and BigInt (ES10). Variables are stored in the stack by value, and can be assigned directly with =.

reference type

There are 1 reference data types: Object. The variable memory address is stored in the stack, and the value is stored in the heap. The assignment of reference type is closely related to the shallow copy and deep copy discussed below.

Shallow copy

First declare the shallow copy ≠ assignment.

Assignment = the memory address of the object is assigned. The two objects point to the same storage space in the heap and affect each other.

let obj1 = {

  name: 'JS',
  
  list: ['JS','CSS', 'HTML']
  
}

let obj2 = obj1

obj2.name = '11'

obj2.list[0] = 'Java'

// {name: '11', list: ['Java', 'CSS', 'HTML']} 

// {name: '11', list: ['Java', 'CSS', 'HTML']} 

Shallow copy creates a new memory space in the heap. After copying, the basic data types of objects do not affect each other, but the reference types still share the same storage space, which will affect each other

function shallowClone (obj1) {

  let obj2 = {}
  
  for (let i in obj1) { 
  
    obj2[i] = obj1[i] 
    
  } 
  
  return obj2;
  
}
let obj1 = {

  name: '11',
  
  list: ['JS','CSS', 'HTML']
  
}

let obj2 = shallowClone(obj1)

obj2.name = 'juejin'

obj2.list[0] = 'Java'

console.log(obj1, obj2)

// {name: '11', list: ['Java', 'CSS', 'HTML']} 
// {name: 'juejin', list: ['Java', 'CSS', 'HTML']} 

Array

Organize shallow copy related APIs of arrays

Extension operator

let arr1 = [1,[2],3]

let arr2 = [...arr1]

arr2[0] = 4

arr2[1].push(5)

console.log(arr1, arr2) // [1,[2,5],3] [4,[2,5],3]

Array.prototype.slice

let arr1 = [1,[2],3]

let arr2 = arr1.slice()

arr2[0] = 4

arr2[1].push(5)

console.log(arr1, arr2) // [1,[2,5],3] [4,[2,5],3]

Array.prototype.concat

let arr1 = [1,[2],3]

let arr2 = arr1.concat([])

arr2[0] = 4

arr2[1].push(5)

console.log(arr1, arr2) // [1,[2,5],3] [4,[2,5],3]

Array.from

Create a new shallow copy array instance of a class array or iteratable object.

let arr1 = [1,[2],3]

let arr2 = Array.from(arr1)

arr2[0] = 4

arr2[1].push(5)

console.log(arr1, arr2) // [1,[2,5],3] [4,[2,5],3]

Array.prototype.map

let arr1 = [1,[2],3]

let arr2 = arr1.map(item => item)

arr2[0] = 4

arr2[1].push(5)

console.log(arr1, arr2) // [1,[2,5],3] [4,[2,5],3]

Array.prototype.filter

let arr1 = [1,[2],3]

let arr2 = arr1.filter(item => item)

arr2[0] = 4

arr2[1].push(5)

console.log(arr1, arr2) // [1,[2,5],3] [4,[2,5],3]

Array.prototype.reduce

reduce may be a bit of a make-up, but it doesn't really reflect its value. It can be regarded as providing a novel idea.

let arr = [1,[2],3]

let arr2 = arr.reduce((arr1,item) => {

  arr1.push(item)
  
  return arr1
  
}, [])

arr2[0] = 4

arr2[1].push(5)

console.log(arr, arr2) // [1,[2,5],3] [4,[2,5],3]

Object

Organize shallow copy related APIs of objects.

Extension operator

let obj1 = {

  name: '11',
  
  list: ['JS','CSS', 'HTML']
  
}

let obj2 = {...obj1}

obj2.name = 'juejin'

obj2.list[0] = 'Java'

console.log(obj1, obj2) 

// {name: '11', list: ['Java', 'CSS', 'HTML']} 

// {name: 'juejin', list: ['Java', 'CSS', 'HTML']} 

Object.assign

let obj1 = {

  name: '11',
  
  list: ['JS','CSS', 'HTML']
  
}
let obj2 = Object.assign({}, obj1)

obj2.name = 'juejin'

obj2.list[0] = 'Java'

console.log(obj1, obj2) 

// {name: '11', list: ['Java', 'CSS', 'HTML']} 

// {name: 'juejin', list: ['Java', 'CSS', 'HTML']} 

Deep copy

Heap memory reopens new memory to store new objects. The two objects will not affect each other.

Array - serialization

Using JSON Stringify converts the array into a JSON string, and then JSON Parse converts the string to a new array.

let arr1 = [1,[2],3]

let arr2 = JSON.parse(JSON.stringify(arr1))

arr2[0] = 4

arr2[1].push(5)

console.log(arr1, arr2) // [1,[2],3] [4,[2,5],3]

Object - serialization
Using JSON Stringify converts the object into a JSON string, and then JSON Parse converts a string to a new object, but this method has disadvantages.

let obj1 = {

  name: '11',
  
  list: ['JS','CSS', 'HTML']
  
}

let obj2 = JSON.parse(JSON.stringify(obj1))

obj2.name = 'juejin'

obj2.list[0] = 'Java'

console.log(obj1, obj2)

// {name: '11', list: ['JS', 'CSS', 'HTML']} 

// {name: 'juejin', list: ['Java', 'CSS', 'HTML']} 

It seems that there is no problem, and there is no need to introduce the lodash library. Now, add a method to the object~

let obj1 = {

  name: '11',
  
  list: ['JS','CSS', 'HTML'],
  
  work: function() {}
  
}

let obj2 = JSON.parse(JSON.stringify(obj1))

console.log(obj1, obj2)

// {name: '11', list: ['JS', 'CSS', 'HTML'], work: function() {}} 

// {name: '11', list: ['JS', 'CSS', 'HTML']} 

Method in JSON After stringify, it is lost... There is always a solution to everything. It really can't lead to the lodash library.

At that time, my processing method was to convert the function into a string to ensure that it is not lost. Finally, I used new Function() to convert the string into a function.

let obj1 = {

  name: '11',
  
  list: ['JS','CSS', 'HTML'],
  
  work: function() {},
  
}
for(let i in obj1) {

  if(typeof obj1[i] === 'function') {
  
    obj1[i] = obj1[i].toString()
  
  }  
}

let obj2 = JSON.parse(JSON.stringify(obj1))

for(let i in obj2) {

  if(typeof obj2[i] === 'string' && obj2[i].indexOf('function') === 0) {
    
    obj2[i] = new Function('return ' + obj2[i])
    
  }   
}

console.log(obj1, obj2)

// {name: '11', list: ['JS', 'CSS', 'HTML'], work: function() {}} 

// {name: '11', list: ['JS', 'CSS', 'HTML'], work: function() {}} 

Of course, this method is not perfect. For example, I do have a field of string type and the value is function. That's a coincidence.

This phenomenon aroused my concern about JSON What types of data will stringify lose? Write attributes are listed

let obj1 = {
  name: '11',
  
  list: ['JS','CSS', 'HTML'],
  
  work: function() {},
  
  date: new Date(),
  
  reg: new RegExp(),
  
  symbol: Symbol(),
  
  number: NaN,
  
  address: Infinity,
  
  age: undefined
  
}

console.log(JSON.stringify(obj1))

// {

// "name":"11",

// "list":["JS","CSS","HTML"],

// "date":"2021-08-29T11:43:33.545Z",

// "reg":{},

// "number":null,

// "address":null

// }

A pit was found

  1. Function, Symbol, undefined missing
  2. NaN and Infinity become null
  3. RegExp object becomes {}
  4. Convert Date object to string
Continue to find JSON on the Internet Stringify stepping on the pit article, you should also be careful in the following situations
    • Method has its own toJSON, which directly returns the return value of the function
    • let obj1 = {
      
        name: 'JS',
        
        toJSON: function() {
        
          return 'JS'
          
        }
      }
      
      console.log(JSON.stringify(obj1)) // JS
      
      1. Property refers to itself, and an error will be reported
      let obj1 = {
      
        name: 'JS',
        
        copy: obj1
        
      }
      console.log(JSON.stringify(obj1)) 
      

      1. Enumerable properties exist and will be lost
      let obj1 = {
      
        name:  'JS'
        
      }
      
      Object.defineProperty(obj1, 'name', {
      
        enumerable: false
        
      })
      console.log(JSON.stringify(obj1)) // {}
      

      For some of these, you can also convert the string first and then return to the original attribute type, which is a kind of idea, but on the premise that the object value is known, otherwise you'd better use cloneDeep in the mature lodash.

    Keywords: Javascript JSON

    Added by xxATOMxx on Sat, 18 Dec 2021 13:03:21 +0200