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:
- 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.
- 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:
- 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
- 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.