Copying an object with a circular reference is an error

The following is a stack overflow error (environment: browser console)

(function a() { a(); }) ();// Uncaught RangeError: Maximum call stack size exceeded
Copy the code

The call stack grows until a limit is reached: browser-hard-coded stack size or memory runs out. To solve this problem, make sure that your recursive function has the basic case that satisfies the above code function A calling itself. The stack overflows.

How does the code above solve the stack burst problem?

With a stop call judgment, there is no stack overflow

(function a(x) {
    if(! x) {return; } a(--x); }) (10);
Copy the code

The following code deep copies an object with a circular reference. An error.

function deepCopy(obj){
	var res = obj.constructor === Array? [] : {};for(var key in obj){
		if(typeof obj[key] === "object"){
			res[key]=deepCopy(obj[key])
		}else{
			res[key]=obj[key]
		}
	}
	return res;
};

var A={
    a:1.b: [1.2.3].c: {"0":0
    },
    d: undefined.e: null.f: new Date()
};
A.A=A;
console.log("A",A);
A.A=A;
var B = clone(A)
console.log(A,B)
// Uncaught RangeError: Maximum call stack size exceeded
// Uncaught scope errors exceeding the maximum call stack size
Copy the code

When deepCopy is called, an error occurs and the stack overflows

An error analysis

  • The A object has A circular reference, and prints it without stack overflow
  • A stack overflow occurs only when A deep copy is made.

Deep copy (fix loop reference error)

Deep copy processing (error handling when the target object has a circular reference)

As we know, an object’s key cannot be an object, as follows:

{{a:2} :1};//Uncaught SyntaxError: Unexpected token ':'

Copy the code

Using a Map key can be a property of an object. Store the target object to be copied as a Key and value as the deep-copied object (there is such a key-value pair in the Map).

function deepCopy(obj, map = new Map(a)) {
  if (typeof obj === 'object') {
     let res = Array.isArray(obj) ? [] : {};
	 if(map.get(obj)){
		return map.get(obj);
	 }
	 map.set(obj,res);
     for(var i in obj){
		res[i] = deepCopy(obj[i],map);
	 }
	 return map.get(obj);
  }else{
	returnobj; }};var A={a:1};
A.A=A;

var B = deepCopy(A);
console.log(B);//{a: 1, a: {a: 1, a: {... }}

Copy the code

Using the browser console above, we can see that we can expand the circular references one by one. No errors will be reported.

The output of the above code in Node looks like this:

{ a: 1.A: [Circular] }
Copy the code

We see that Circular means that the attribute of A circulates through itself. The property of A is equal to the input object A itself. Informing directly through Circular refers to itself circularly.

The difference between browser and Node output

  • The browser normally outputs the processed circular reference object.
  • Nodes are identified by cirular as circular references.

Copy normal objects with a circular reference to the deepCopy version.

function deepCopy(obj, map = new Map(a)) {
  if (typeof obj === 'object') {
     let res = Array.isArray(obj) ? [] : {};
	 if(map.get(obj)){
		return map.get(obj);
	 }
	 map.set(obj,res);
     for(var i in obj){
		res[i] = deepCopy(obj[i],map);
	 }
	 console.log("map",map);
	 return map.get(obj);
  }else{
	returnobj; }};var A = {a:1.b: [1.2.3]};
var B = deepCopy(A);
console.log(B); / / {a: 1, b: [1, 2, 3]}

Copy the code

What is the map printed out from the above code?

[[{a:1.b: [1.2.3] {},a:1.b: [1.2.3]}], [[1.2.3], [1.2.3]]]Copy the code

We can see that the map stores the same data as the key and value of two reference types, including the target object. It’s like, you have an object

 var A={a:1}; A.A=A; A.B=A;
    
Copy the code

We can see that A has A property on it and A is the OBJECT of A. So, the map already has an A object like A key and value. Your property A has A value in the map, so return the object A that already exists. That’s A reference to object A itself.

If you enter data [1,2,3], the map will store [[1,2,3],[1,2,3]]

Because of this, we can use maps to solve the problem of circular references

1) Solve circular references

// Use the Map function
function deepCopy(obj,map = new Map(a)){
    if (typeofobj ! ='object') return 
    var newObj = Array.isArray(obj)? [] : {}if(map.get(obj)){ 
      return map.get(obj); 
    } 
    map.set(obj,newObj);
    for(var key in obj){
        if (obj.hasOwnProperty(key)) {
            if (typeof obj[key] == 'object') {
                newObj[key] = deepCopy(obj[key],map);
            } else{ newObj[key] = obj[key]; }}}return newObj;
}
const obj1 = {
        x:1.y:2.d: {a:3.b:4
         }
    }
  obj1.z = obj1;
  const obj2 = deepCopy(obj1);
  console.log(obj2)
            
/ / output node {x: 1, y: 2 d: 4} {3, a: b:, z: [Circular]}
// Console output {x: 1, y: 2, d: {... }, z: {... }}
Copy the code

Conclusion:

  • return map.get(obj); Returning this is the same thing as returning res. The data stored in the map is the same key-value pair as the key and value, including the target object to be copied.
  • How does Map address circular references?

By storing key-value pairs of the same object. Include the object you end up returning from your deep copy. So you have a key-value pair in your Map where key is the target object and value is the target object. When there is a circular reference, a recursive call, it adds a condition to return the object if it is present in the map. The premise is that every time we recurse, we save this object as key, and value as the key-value pair of this object in the Map.

Communicate and learn from each other

You can also follow my GitHub, exchange learning progress ~