How to implement deep copy
Object.assign()
Can only handle one layer of deep copy
var oldObj = {a:1};
var newObj = Object.assign({},oldObj)
console.log(oldObj===newObj)//false
Sequence/Desequence
It is not possible to clone special objects such as functions and RegExp.
The object’s constructor will be discarded, and all constructors will point to Object
If the object has a circular reference, an error will be reported.
This method can only correctly handle objects such as Number, String, Boolean, Array, and flat objects, that is, data structures that can be directly represented by json.
// Constructor function person(pname) { this.name = pname; }
const Messi = new person(‘Messi’);
// Function function say() { console.log(‘hi’); };
const oldObj = { a: say, b: new Array(1), c: new RegExp(‘ab+c’, ‘i’), d: Messi };
const newObj = JSON.parse(JSON.stringify(oldObj));
// Unable to copy function console.log(newObj.a, oldObj.a); // undefined [Function: say] // Sparse array copy error console.log(newObj.b[0], oldObj.b[0]); // null undefined // Unable to copy regular object console.log(newObj.c, oldObj.c); // {} /ab+c/i // Constructor pointing to error console.log(newObj.d.constructor, oldObj.d.constructor); // [Function: Object] [Function: person]
Recursive copy
function isArray(arr) {
return Object.prototype.toString.call(arr) == '[object Array]'
};
function _clone(oldObj) {
console.log(typeof oldObj)
if (typeof oldObj !== "object") {
return oldObj;
}
var obj = isArray(oldObj) ? [] : {}
for (var i in oldObj) {
if (oldObj.hasOwnProperty(i)) {
if (oldObj[i] == oldObj) {
continue
}
obj[i] = _clone(oldObj[i])
}
}
return obj
}
//Still use the above test
function Person(pname) {
this.name = name;
}
const Messi = new Person('Messi');
function say() { console.log('hi');
}; const oldObj = { a: say, b: new Array(1), c: new RegExp(‘ab+c’, ‘i’), d: Messi }; const newObj = _clone(oldObj) // Functions can be copied console.log(newObj.a, oldObj.a); // [Function: say] [Function: say] // Sparse arrays can be copied console.log(newObj.b[0], oldObj.b[0]); // undefined undefined // Regular objects cannot be copied console.log(newObj.c, oldObj.c); // {} /ab+c/i // Constructor pointing to error console.log(newObj.d.constructor, oldObj.d.constructor); // [Function: Object] [Function: person] // Circular references are not allowed either. Removing the continue above will cause memory overflow.
There are also issues like being unable to copy regular objects and constructor pointer errors.
Improved Recursive Copy
const isType = function (obj, type) {
//Function used to distinguish attribute types
if (typeof obj !== 'object') return false
const typeString = Object.prototype.toString.call(obj);
let flag;
//flag is used as the identifier for attribute judgment
switch (type) {
case 'Array':
flag = typeString === '[object Array]';
break;
case 'Date':
flag = typeString === '[object Date]';
break;
case 'RegExp':
flag = typeString === '[object RegExp]';
break;
case 'Set':
flag = typeString === '[object Set]';
break;
case 'Map':
flag = typeString === '[object Map]';
break;
case 'Symbol':
flag = typeString === '[object Symbol]';
break;
default:
flag = false;
}
return flag;
}
function _clone(oldObj) { let obj; if (isType(oldObj) == false) { //Basic data types, functions, and types I haven’t considered are all directly referenced (lazy) obj = oldObj } else if (isType(oldObj, ‘Array’)) { //Process arrays obj = []; } else if (isType(oldObj, ‘RegExp’)) { //Process regular expressions let flags = ‘’; if (oldObj.global) flags += “g”;//Global if (oldObj.ignoreCase) flags += “i”;//Case if (oldObj.multiline) flags += “m”;// return obj = new RegExp(oldObj.source, getRegExp(oldObj)); } else if (isType(oldObj, ‘Date’)) { //Processing time object return obj = new Date(oldObj.getTime()); } else if (isType(oldObj, ‘Set’)) { //Processing Set return obj = new Set([…obj]); } else if (isType(oldObj, ‘Map’)) { //Processing Map return obj = new Map([…obj]); } else if (isType(oldObj, ‘Symbol’)) { //Processing Symbol return obj = Symbol(String(oldObj).slice(7, -1)) } else { // Processing object prototype let proto = Object.getPrototypeOf(oldObj); obj = Object.create(proto); } for (var i in oldObj) { if (oldObj.hasOwnProperty(i)) { if (oldObj[i] == oldObj) { obj[i] = oldObj } else { obj[i] = _clone(oldObj[i]) } } } return obj }
function Person(pname) {
this.name = pname;
}
const Messi = new Person('Messi');
function say() {
console.log('hi');
};
const oldObj = {
a: say,
b: new Array(1),
c: new RegExp('ab+c', 'i'),
d: Messi,
e:new Set([1,2,3]),
f:new Map([['foo', 1],['bar', 2]]),
g:Symbol("yosgi")
};
const newObj = _.clone(oldObj)
// function console.log(newObj.a, oldObj.a); // [Function: say] [Function: say]
// Sparse array console.log(newObj.b[0], oldObj.b[0]); // undefined undefined // Regular object console.log(newObj.c, oldObj.c); // /ab+c/i /ab+c/i // Constructor console.log(newObj.d.constructor, oldObj.d.constructor); // [Function: person] [Function: person] //Set console.log(newObj.e, oldObj.e)//Set(3) {1, 2, 3} Set(3) {1, 2, 3} //Map console.log(newObj.f, oldObj.f)//Map(2) {“foo” => 1, “bar” => 2} Map(2) {“foo” => 1, “bar” => 2} //Symbol console.log(newObj.g, oldObj.g) //Symbol(yosgi) Symbol(yosgi) //Circular references // var oldObj = {};; // oldObj.a = oldObj; // const newObj = _.clone(oldObj) // console.log(oldObj,newObj) //{a: {…}} {a: {…}}
Functions, arrays, regular objects, constructors, Sets, Maps, Symbols, and circular references are all considered
However, there are also Buffer, Promise, proxy, etc…
lodash
Really delicious
function person(pname) { this.name = pname; } const Messi = new person(‘Messi’); function say() { console.log(‘hi’); }; const oldObj = { a: say, b: new Array(1), c: new RegExp(‘ab+c’, ‘i’), d: Messi }; const newObj = _.clone(oldObj) // Function console.log(newObj.a, oldObj.a); // [Function: say] [Function: say] // Sparse array console.log(newObj.b[0], oldObj.b[0]); // undefined undefined // Regular expression object console.log(newObj.c, oldObj.c); // /ab+c/i /ab+c/i // Constructor console.log(newObj.d.constructor, oldObj.d.constructor); // [Function: person] [Function: person] //Circular reference var oldObj = {};; oldObj.a = oldObj; const newObj = _.clone(oldObj)