preface

Light and dark copies of objects are a perennial topic, and with so many articles on the platform, it’s almost impossible to break through this material. Simply I write an article, accumulate their own learning experience, so that when the follow-up review, can have a more clear thinking.

define

Shallow copy: Reference all the data in the data and point to the same storage address. After the copied data is modified, the original data will have side effects.

Deep copy: Copies all the data in the data. Modifying the data after the copy does not affect the original data.

A deep copy

In business, a lot of times you’re making shallow copies, and if it doesn’t affect business logic, you probably don’t care about these things.

The equal sign assignment

Equal-sign assignments of reference types are the most common shallow copies, as follows:

var obj = {
  name: 'Nick'
}

var newObj = obj
Copy the code

If you change newobj.name = ‘Chen’, obj will also change. This is because the declared obj is a reference variable that exists in the global heap. Assigning to newObj simply assigns the address of memory to it, so changing the properties of newObj is changing the properties of the data in the heap, and obj will change as well.

var obj = {
  name: 'Nick'
}

var newObj = obj

newObj.name = 'Chen'

console.log(obj.name) // 'Chen'
console.log(newObj.name) // 'Chen'
Copy the code

Object.assign

You think object. assign is a deep-copy method, but it’s not. It is also a shallow copy, only the first level of the original type of data, not implicated, the reference type will still be tampered with, we use data to speak:

var obj = {
  name: 'Nick'.hobby: ['code'.'movie'.'travel', { a: 1}}]var newObj = Object.assign({}, obj)

newObj.name = 'Chen'
newObj.hobby[0] = 'codeing'
newObj.hobby[3].a = 2

console.log('obj', obj)
console.log('newObj', newObj)
Copy the code

The print result is as follows:

The green arrow represents the original type and has not been tampered with. The red arrows represent the reference types, which change as newObj changes.

. Extended operator

It is special, if the first layer of the object to be copied is the original type, it is a deep copy. If it is a reference type, it is a shallow copy. Try a little experiment:

var obj = {
  name: 'Nick'.salary: {
  	high: 1.mid: 2.low: 3}}varnewObj = { ... obj } newObj.name ='Chen'
newObj.salary.high = 2

console.log(obj)
console.log(newObj)
Copy the code

The name attribute of obj was not changed, and the high in salary was changed to 2.

So if we want to use… To make a deep copy, extend the operator like this:

var obj = {
  name: 'Nick'.salary: {
  	high: 1.mid: 2.low: 3}}varnewObj = { ... obj,salary: {
  	...obj.salary
  }
}
Copy the code

I think there’s something wrong with anyone who operates like that.

JSON.parse + JSON.stringify

Many ambitious people will use this approach to deep copy code. Of course, in most business scenarios, this works better, but there are still situations where problems can arise, large and small.

The object has functions:

var obj = {
  name: 'Nick'.hobby: ['code'.'movie'.'travel', { a: 1}].callback: function() {
    console.log('test')}}var newObj = JSON.parse(JSON.stringify(obj))

newObj.name = 'Chen'
newObj.hobby[0] = 'codeing'
newObj.hobby[3].a = 2

console.log('obj', obj)
console.log('newObj', newObj)
Copy the code

The data is out of control, but the function callback is out of control.

Object contains the time object Date

var obj = {
  name: 'Nick'.date: [new Date(1621259998866), new Date(1621259998866)]};var newObj = JSON.parse(JSON.stringify(obj))
Copy the code

The time object inside date in obj is executed.

The object contains RegExp and Error

var obj = {
  name: 'Nick'.date: new RegExp('\\s+'),};var newObj = JSON.parse(JSON.stringify(obj));
obj.name = 'Chen'
Copy the code

After copying, date becomes a null value.

Object has undefined value

var obj = {
	name: undefiend
}

var newObj = JSON.parse(JSON.stringify(obj));
Copy the code

The undefiend is lost in the process of copying.

The object has NaN, Infinity, -infinity

var obj = {
  name1: NaN.name2: Infinity.name3: -Infinity
}

var newObj = JSON.parse(JSON.stringify(obj))
Copy the code

Go straight intonullI don’t want to laugh with you, but I don’t think that’s often the case.

Object contains objects produced by the constructor

function Animal(name) {
	this.name = name
}

var animal = new Animal('dog')

var obj = {
	test: animal
}

var newObj = JSON.parse(JSON.stringify(obj))
Copy the code

The constructor is lost, and after copying it, it points directly to Object.

Situations like these may not happen very often in a real development environment, but when you do, it can take unnecessary time to figure out what the problem is without your knowledge.

Drop deep copy hard

First, you can use a tool like Lodash. CloneDeep to implement deep copy.

Here I’m going to manually write a deep copy, from which you can learn some small points, if you like, I’ll write it for myself.

var obj = {
  name: 'Nick'.date: [new Date(1621261792177)].callback: function() { console.log('shadiao')},link: undefined
}

function deepClone(origin) {
  if(origin === null) return null 
  if(typeoforigin ! = ='object') return origin;
  if(origin.constructor === Date) return new Date(origin); 
	// Take two arguments, origin is the original object
  var _target = origin.constructor() // Maintain the inheritance chain
  Origin / / cycle
	for(var key in origin) {
    // Do not iterate over properties on its prototype chain
    if (origin.hasOwnProperty(key)) {
    	// If origin[key] is a reference type value, then recursive logic is entered
      if (typeof origin[key] === 'object'&& origin[key] ! = =null) {
        Origin [key] = _target[key];
        // Note that the first declaration of _target will carry through the recursion, and all subsequent assignments will be returned to _target
        _target[key] = deepClone(origin[key])
      } else {
        // If it is not an object or array, go to this logic and assign _target[key] directly.
        _target[key] = origin[key]
      }
    }
  }
  // for... After the in loop completes, return the _target value of the current context
  return _target
}

const newObj = deepClone(obj)


Copy the code

The properties of the obJ object above are copied in full.

There is a key step in the above code, and if you understand it, you will basically understand why recursive assignment is possible. Let’s look at the following code:

function test() {
	var obj = {}
  const _obj = test1(obj)
  console.log('obj', obj)
  console.log('_obj', _obj)
	console.log(_obj === obj)
}

function test1(_obj) {
	_obj.a = 1
  return _obj
}

test()
Copy the code

This code declares an obj object inside the test function and passes it as an argument to the test1 method. Test1 internally assigns an A attribute to the _obj argument passed in and returns _obj.

Obj is also added with the a attribute, and _obj is equal to obj. This means that they point to the same memory address, which is the function scope within test. In JavaScript Advanced Programming, on page 86, there is a detailed analysis of the passing of reference types between functions.

Using this principle, the _target[key] passed in the deepClone recursion is actually the reference type variable of the first deepClone operation, and the subsequent recursive assignment of _target[key]. Will be reflected in the original _target. At the end of the function execution, return _target is the final value of the final recursive deep copy.

conclusion

This point is so detailed that I’m not sure I’ll use it in a lot of business development. But at least when you’re faced with this kind of problem, you’re not going to feel like you’re not cut out for the industry. Once again, basic knowledge is very important, do not look down upon these ordinary insignificant knowledge, really to the bayonet, you know nothing.

Previous good articles recommended

Get through the front-end environment variable – env likes 👍 228

Vite 2.0 + React + Ant Design 4.0 build development environment 👍 385

Face not interview, you have to understand the prototype and prototype chain like number 👍 593

Vue 3 and Webpack 5 are coming, manual build knowledge should be updated with likes at 👍 521

For another point of view, webpage performance optimization number of likes 👍 200

Hi, tell me about your understanding of front-end routing. Likes 👍 625

I didn’t have a choice before, now I just want array.prototype. reduce 👍 588

The ubiquitous publish-subscribe model — this time it must get 👍 164 likes

JSX and virtual DOM likes 👍 110