Deep copy and shallow copy

Review:

  • Basic data types: Basic data types refer to simple data segments that are stored on a stack and accessed by value.
  • Reference data type: it is stored in the heap, and it holds a pointer on the stack to its data in the heap;

The difference between deep copy and shallow copy:

  1. A shallow copy copies only one layer of objects, and if there is a nesting of objects, the shallow copy behaves like an assignment before the nesting. That is, when a primitive type is encountered in a copy object, the value of the primitive type is copied. When a reference type is encountered, the memory address is copied again.
  2. Deep copy solves the problem that shallow copy copies only one layer of objects. It copies an object out of memory and places it in a new area to store the new object.

Direct assignment

// Let a = 1; let b = a; b = 2; console.log(a, b); // let arr = [0,1,2]; let brr = arr; brr[0] = 1; console.log(arr, brr);Copy the code

Console:

1 2

,1,2,1,2 [1] [1]

Obviously, when changing BRR, it assigns a pointer to ARR on the stack (address), so it is the same reference to ARR, and they both refer to the same block of heap memory.

Shallow copy

Object.assign()

Used to assign the values of all enumerable properties from one or more source objects to the target object. It will return the target object.

Grammar:

Object.assign(target, ... sources)Copy the code

Example:

const obj = {name: "dzz", where: "juejin"};
const obj2 = Object.assign({}, obj, {name: "yly"});
console.log(obj, obj2);
Copy the code

Console:

{name: “dzz”, where: “juejin”} {name: “yly”, where: “juejin”}

Concat Shallow copy array

Used to merge two or more arrays. This method does not change an existing array, but returns a new array.

Const arr = [0]; const arr2 = arr.concat(); arr2[0] = 1; console.log(arr, arr2);Copy the code

Slice shallow copy

Returns a shallow copy of a new array determined by begin and end

Const arr = [0]; const arr2 = arr.slice(); arr2[0] = 1; console.log(arr, arr2);Copy the code

. Expansion operator

Let arr = [0]; let arr2 = [...arr]; arr2[0] = 1; console.log(arr, arr2);Copy the code

Its implementation

function shallowCopy(target) { //1. Ensure that the target passed is of reference type if(typeof target! == "object" || target === null) { return target; Const copyTarget = array.isarray (target)? [] : {}; for(let item in target) { //3. If (target.hasownProperty (item)) {copyTarget[item] = target[item]; if(target.hasownProperty (item)) {copyTarget[item] = target[item]; } } return copyTarget; }Copy the code

Expand the for… In and for… of

for… In: Traverses the enumerable properties of an object in any order except Symbol

Grammar:

for (variable in object)
Copy the code

Variable: At each iteration, variable is assigned a different attribute name

Object: An object whose enumerable properties of a non-symbol type are iterated over

Best not used for Array

for… Of: Creates an iteration loop on iterable objects (including Array, Map, Set, String, Arguments, and so on), calls custom iteration hooks, and executes statements for the values of each different property

Grammar:

for (variable of iterable)
Copy the code

Variable: In each iteration, the values of different attributes are assigned to variables.

Iterable: An object whose properties are iteratively enumerated.

Testing:

Let arr = [1, 2, 3]; for(let item of arr) { console.log(item); //123 } for(let item in arr) { console.log(item); / / 012}Copy the code

Deep copy

JSON. The parse () and JSON. Stringfy ()

Json.stringfy () : Converts a JavaScript object or value to a JSON string, optionally replacing the value if a replacer function is specified, or optionally containing only the properties specified by the array if the specified replacer is an array.

Grammar: developer.mozilla.org/zh-CN/docs/…

JSON.stringify(value[, replacer [, space]])
Copy the code

Value: The value to be serialized into a JSON string.

Replacer: If this parameter is a function, each attribute of the serialized value is converted and processed by the function during serialization; If the parameter is an array, only the property names contained in the array will be serialized into the final JSON string; If this parameter is null or not provided, all attributes of the object are serialized.

Space: Embellishes the output by specifying a blank string

Note:

  • Undefined, arbitrary functions, and Symbol values are ignored during serialization. But the function, undefined, returns undefined when converted separately.
  • Object containing reference objects that execute this method throw an error
const obj = {val: "juejin"}; obj.target = obj; console.log(JSON.stringfy(obj)); / / an errorCopy the code

Console: json. stringfy is not a function

  • The Date calls toJSON() to convert it to a string, which is treated as a string.

Json.parse () : Parses JSON strings

Grammar: developer.mozilla.org/zh-CN/docs/…

JSON.parse(text[, reviver])
Copy the code

Text: String to be parsed into JavaScript values

Reviver: Used to modify the original value generated by parsing before the parse function returns

Parse (json.stringfy ()) is used for deep copy. Functions, undefined, Symbol, Data, etc. cannot be properly handled.

const obj = {
    name: "dzz",
    age: 12,
    regexp: /\d/,
    tfn: function() {},
    tunde: undefined,
    tnull: null,
    tdate: new Date(),
    syl: Symbol("juejin"),
}
const obj2 = JSON.parse(JSON.stringify(obj));
console.log("obj", obj);
console.log("obj2:", obj2);
Copy the code

Console: obj {AGE: 12,name: “DZZ “,regexp: /\ D /, SYL: Symbol(juejin), Tdate: Tue Aug 31 2021 10:22:18 GMT+0800 {}, TFN: ƒ (),tnull: null,tunde: undefined}

Obj2 {age: 12,name: “DZZ “, regEXP: {},tdate:” 2021-08-3t02:22:18.907z “,tnull: null}

The result is that the re becomes an empty object. Functions, undefined and Symbol are ignored, and dates are treated as strings.

That’s the flaw (and that circular reference)

Its implementation

Start by thinking that you can use shallow copy to copy the current layer recursively (self-implementation of shallow copy)

Simple implementation:

function deepCopy(target) { //1. Ensure that the target passed in is a reference type and the base data type is returned if(typeof target! == "object" || target === null) { throw new TypeError("err"); Const copyTarget = array.isarray (target)? [] : {}; for(let item in target) { //3. If (target.hasownProperty (item)) {//4. Return copyTarget[item] = target[item]; return copyTarget[item]; } } return copyTarget; } / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- testing const obj = {name: "DZZ," child: {name: "XXX"}, top: undefined, tnull: null, a: Symbol("juejin"), }; const obj2 = shallowCopy(obj); const obj3 = deepCopy(obj); obj.child.name = "xx2"; console.log(obj2); //name changed to xx2 console.log(obj3); // Not modifiedCopy the code

Think of a problem to solve: Circular reference

let a = Symbol("juejin"); const obj = { name: "dzz", child: { name: "xxx" }, tun: undefined, tnull: null, a: 1, }; Target = obj; const obj2 = shallowCopy(obj); const obj3 = deepCopy(obj); obj.child.name = "xx2"; console.log(obj2); //name changed to xx2 console.log(obj3); // Not modifiedCopy the code

Console: Error Maximum Call Stack Size exceeded

We can store a copy of the object in the hash table, and if the current object has been copied, return the copied hash table

Function deepCopy(target, myMap = new WeakMap()) {//1. Ensure that the target passed is of reference type if (typeof target! == "object" || target === null) { return target; If (mymap.get (target)) {return mymap.get (target); Const copyTarget = array.isarray (target)? [] : {}; Mymap.set (target, copyTarget); for (let item in target) { //4. If (target.hasownProperty (item)) {//5. CopyTarget [item] = deepCopy(target[item], myMap); } } return copyTarget; } / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- testing const obj = {name: "DZZ," child: {name: "XXX"}, top: undefined, tnull: null,}; Target = obj; const obj3 = deepCopy(obj); obj.child.name = "xx2"; obj.target.name = "xx3"; console.log(obj3);Copy the code

Differences between WeakMap and Map:

  1. A WeakMap object is a set of key/value pairs where the keys are weakly referenced. The key must be an object, and the value can be arbitrary. Map Any value can be a key or a value
  2. WeakMap holds a “weak reference” to each key object, which allows garbage collection to proceed correctly when no other reference exists. The key it uses for mapping is valid only if it is not reclaimed, i.e. WeakMap cannot be traversed,

Think again: Copy the Symbol value and regular expression inside the object as well

Simple implementation:

function deepCopy(target, myMap = new WeakMap()) { //1. Ensure that the target passed is of reference type if (typeof target! == "object" || target === null) { return target; If (mymap.get (target)) {return mymap.get (target); If (target instanceof Date) return new Date(target); if (target instanceof Date) return new Date(target); if (target instanceof RegExp) return new RegExp(target); Const copyTarget = array.isarray (target)? [] : {}; Mymap.set (target, copyTarget); for (let item in target) { //4. If (target.hasownProperty (item)) {//5. CopyTarget [item] = deepCopy(target[item], myMap); } } return copyTarget; }Copy the code

Testing:

const obj = {
    name: "dzz",
    child: {
        name: "xxx"
    },
    tun: undefined,
    tnull: null,
    a: Symbol("a"),
    b: Symbol.for("b"),
    fn: function () { },
    tDate: new Date(),
    treg: /\d/,
};
console.log(JSON.parse(JSON.stringify(obj)));
const obj2 = deepCopy(obj);
console.log(obj2);
Copy the code

conclusion

Deep copy and shallow copy still have a lot to learn.