Copy, in JS, is divided into shallow copy and deep copy. How are these two differentiated? And how is it achieved?
The distinction between shades of a copy
First of all, in JS, is divided into basic data types and complex data types,
Underlying data type: number, String, Boolean, undefined, null, Sysmbol
Complex data types: Object, Array, Function, date, and so on
The underlying data type value, stored in the stack and copied, will re-create the same space in the stack to store the data. For complex data types, values are stored in the heap, where the reference address of the value is stored in the stack. A depth copy, only for complex data types.
A shallow copy of the shallowcopy is a bit-wise copy of an object. Creates a new object that has an exact copy of the original object. If any field of an object is a reference to another object, only the reference address is copied, that is, only the memory address is copied, not the object itself, and the old or new object shares the same block of heap memory. Change one of the objects and the other will also be affected. If there are changes, the original data is lost.
Deep copy deepcopy, copy a brand-new object instance, the new object and the original object does not share memory, the operation does not affect each other.
Simple point to distinguish,
shallow copy copy reference;
Deep copy copy instance.
Shallowcopy shallow copy Implementation mode 1. Assign value
First of all, the simple assignment situation,
var o1 = { a : 1, b : 2 }var o2 = o1console.log(o2 === o1) // trueo1.a = 2console.log(o1) // {a: 2, b: 2}console.log(o2) // {a: 2, b: 2}
Assignment, which is a reference to an object's address, changes the value of an object, and the value of another copied object changes, so this is a shallow copy.
2. Array.concat ()
The Concat method is used to combine two or more arrays. The method does not change an existing array, but simply returns a copy of the concatenated array.
var o1 = [1, [2], 3]var o2 = o1.concat([]) // 这里会返回一个o1对象的浅拷贝对象console.log(o2) // [1, [2], 3]console.log(o1 === o2) // false
The O2 array is a new array. If you change the O1 array object, will it affect the O2 array object?
o1[0] = 11console.log(o1) // [11, [2], 3]console.log(o2) // [1, [2], 3]
In this case, the O2 array value is not changed. This is because the first element in O2 and O1 is not the same memory address.
o1[1][0] = 22console.log(o1) // [11, [22], 3]console.log(o2) // [1, [22], 3]
Instead of modifying the value of the reference in the O1 variable, the O2 array value changes as well. This means that the second element in O2 and the second element in O1 refer to the same memory address.
According to the above note, it can be concluded that if the array is a one-dimensional array, it can be considered a deep copy. If it is a multidimensional array, it is a shallow copy.
3. Array.slice ()
The slice method can return the selected element from an existing array.
var o1 = [1, [2], 3]var o2 = o1.slice(0)console.log(o1) // [1, [2], 3]console.log(o2) // [1, [2], 3]
Instead of modifying the array, the method returns a sub-array.
o1[0] = 11console.log(o1) // [11, [2], 3]console.log(o2) // [1, [2], 3]
As seen from the results, only the value of O1 was modified, and the value of O2 was not modified.
o1[1][0] = 22console.log(o1) // [11, [22], 3]console.log(o2) // [1, [22], 3]
The result shows that the values of the two variables of O1 and O2 have changed. Description, both references point to the same memory address.
Above, the description is a shallow copy.
4. Object.assign ()
The Object.assign () method is used to copy the values of all enumerable own properties from one or more source objects to the target object. It returns the target object
var o1 = { a : 1, b : { c : 2, d : 3} }var o2 = Object.assign({}, o1)console.log(o1) // { a : 1, b : { c : 2, d : 3} }console.log(o2) // { a : 1, b : { c : 2, d : 3} }console.log(o2 === o1) // false 说明实现了浅拷贝o1.a = 11console.log(o2) // { a : 1, b : { c : 2, d : 3} } o1和o2内部包含的基本类型值,拷贝的是其实例,不会相互影响o1.b.c = 22console.log(o1) // { a : 11, b : { c : 22, d : 3} }console.log(o2) // { a : 1, b : { c : 22, d : 3} } o1和o2内部包含的引用类型值,拷贝的是其引用,会相互影响
5. Using the Extend function in jquery
// Shallow copyjQuery.extend({},OriginalObject)// Deep copy jQuery.extend(true, {},OriginalObject)
jQuery.extend( [deep ], target, object1 [, objectN ] )
, where Deep is a Boolean type and, if true, deeply copied.
var $ = require('jquery')var o1 = { a : 1, b : { c : 2 } }var o2 = $.extend({}, o1)console.log(o1.b === o2.b) // trueconsole.log(o1.a === o1.a) // false
6. _.clone in Lodash ()
The use of structured copy algorithms. Support Copy Arrays,array Buffers,booleans, data objects, maps,
Numbers, Object
objects, regexes, sets, strings, symbols, and typed
Arrays. An arguments
enumerable property of an object is copied as a normal object.
Returns an empty object for non-copied values such as Error objects, functions, DOM nodes, and weak mappings.
Shallow copy:_.clone()
Deep copy:_.cloneDeep()
var objects = [{ 'a': 1 }, { 'b': 2 }]; var shallow = _.clone(objects);console.log(shallow[0] === objects[0]); // trueobjects[0].a = 11console.log(shallow[0]) // { a : 11}
Deepcopy deep Copy Implementation mode 1. Copy manually
To achieve a copy of the copy, not affected by the original, then you can do so
var o1 = { a : 1, b : 2 }var o2 = { a : o1.a, b : o1.b }console.log(o2 === o1) // falseo1.a = 2console.log(o1) // {a: 2, b: 2}console.log(o2) // {a: 1, b: 2}
A deep copy is implemented by copying values for each reference object.
2. Json.parse (Json.stringify (Object_array))
var o1 = { a : 1, b : { c : 2} }var o2 = JSON.parse(JSON.stringify(o1))console.log(o1 === o2) // falseconsole.log(o1.b === o2.b) // falseo1.b.c = 22o1.a = 11console.log(o1) // { a : 11, b : { c : 22} }console.log(o2) // { a : 1, b : { c : 2} }
This way, only for types that can be converted to JSON objects, such as Array,object. If a function is encountered, it does not apply.
3. Meet the Jquery.extend () method again
jQuery.extend( [deep ], target, object1 [, objectN ] )
, where Deep is a Boolean type and, if true, deeply copied.
Jquery.extend () source jquery.extend = JQuery.fn.extend = function () {var options, name, SRC, copy, Copyisarray, clone, target = arguments[0] | | {},//define variable, get first parameter. The default is an empty object. i = 1, length = arguments.length, deep = false; Handle a deep copy situation handles the dark copy if (typeof target = = = "Boolean") {depth = target; Skip the Boolean and the target//skip Boolean and destination, re-assign target target = arguments[I] | | {}; i++; }//Handle case if Target is a string or something (possible in depth copy)//When the target is a string or other (may be used in the deep copy) processing use cases/ /When the target is non-object and is not a function when handling if (typeof target!== "Object" &&!jquery.isfunction (target)) {target = {}; }//Extend jquery itself if only one argument is passed//if you only pass a parameter, extend jQuery itself if (i = = length) { target = this; i--; } for (; i < length; i++) {//* deal with non-null/undefined values//processing only non-null/undefined value if (options = arguments[I] = null) {//Extend the Base Object//Expand base/Source Object for (name in options) {src = target[name]; copy = options[name]; Prevent never-ending loop//Prevent infinite loop if (target = = = copy) {continue; }//Recurse If we ' re merging plain objects or arrays//If you want to merge pure objects or arrays, use recursion if (deep && copy && jquery.isplainobject (copy) | | (Copyisarray = Array.isarray (copy))) {if (copyisarray) {Copyisarray = false; clone = src && array.isarray (src)? SRC: []; } else {clone = src && jquery.isplainobject (src)? src: {}; }//Never move original objects, clone them Do not move the original object, copy them target[name] = Jquery.extend (deep, clone, copy); Don ' t bring in undefined values//do not introduce undefined value} else if (copy!== undefined) { target[name] = copy; }}}}//Return the Modified object//return target after the modification;};
4. _.clonedeep in Lodash ()
Using the third-party library Lodash, its deep copy function Clonedeep (), this function is still relatively reliable, most of the requirements can be satisfied.
var o1 = { a : 1, b : { c : 2} }var o2 = _.cloneDeep(o1)console.log(o1 === o2) // falseo1.a = 11o1.b.c = 22console.log(o1) // { a : 11, b : { c : 22} }console.log(o2) // { a : 1, b : { c : 2} }
5. Make a deep copy of yourself
For the complex types of array and object two, you implement a deep copy yourself. Self-fulfilling deep copy, compared to jquery's. Undefined and null values are not considered.
// 检测数据类型的函数function typeString(obj) { var cons = Object.prototype.toString.call(obj).slice(8, -1) return (cons === 'Array' || cons === 'Object')}// 实现深度拷贝 Array/Objectfunction deepClone(oldObj) { if(typeTrue(oldObj)) { var newObj = oldObj.constructor() for(let i in oldObj) { if (oldObj.hasOwnProperty(i)) { newObj[i] = typeTrue(oldObj[i]) ? deepClone(oldObj[i]) : oldObj[i] } } return newObj; } else { return oldObj }}// 测试var o1 = [1, 2, [3, 4]]var o2 = deepClone(o1)console.log(o1 === o2) // falseo1[2][0] = 2018console.log(o2) // [1, 2, [3, 4]]console.log(o1) // [1, 2, [2018, 4]]
Depth Copy Summary
It is an important indicator that the copy will affect each other. The depth copies discussed above are small in scope, most of which only consider object and array types, but can be used in most cases.
Function, Data, regexp, etc. are not considered. If you need to consider these, can be specific to specific situations, such as Lodash _.clone, is the use of structured copy algorithm, for different situations to copy.
Shallow copy shallowcopy and deep copy deepcopy in JavaScript