Jun 01, 2021 Article blog
In JavaScript development, we often encounter situations where deep copying is required, and we often ask this question in interviews, so what is a shallow copy and what is a deep copy?
With regard to the concept of shallow copying, I saw a statement on the Internet that goes directly to the code.
var person = {name: "Jason", age: 18, car: {brand: "Ferrari", type: "430"}};
var person1 = person; //他们认为这是浅拷贝
But I personally think that this one above doesn't involve a copy at all, it's just a simple reference assignment. In my understanding, a shallow copy should be a property that does not take into account the reference type of the object, and only copies all members of the current object, as follows:
function copy(obj){
var objCopy = {};
for(var key in obj){
objCopy[key] = obj[key];
}
return objCopy;
}
var person = {name: "Jason", age: 18, car: {brand: "Ferrari", type: "430"}};
var personCopy = copy(person);
In this code above, the
person
object has two basic types of properties
name
and
age
a reference type property
car
and when copied using methods such as the one above,
name
and
age
properties are copied normally, but
car
property only makes copies of references, which causes the copied
personCopy
and
person
to share a
car
object.
This is called a shallow copy.
Deep copy is when copying, you need to copy the properties of all reference types within the object you are currently copying, which means that no data is shared between the copied object and the original object, and everything is exclusive to itself.
There are several factors to consider when implementing a deep copy:
We can do deep replication through
$.extend()
method. T
hankfully, we can recursive
extend
by adding a parameter in
jQuery
C
all
$.extend(true, {}, ...)
Deep replication is possible, refer to the following example:
var x = {
a: 1,
b: { f: { g: 1 } },
c: [ 1, 2, 3 ]
};
var y = $.extend({}, x), //shallow copy
z = $.extend(true, {}, x); //deep copy
y.b.f === x.b.f // true
z.b.f === x.b.f // false
But
jQuery
$.extend()
approach has drawbacks, and what are the drawbacks?
Let's look at the following example:
var objA = {};
var objB = {};
objA.b = objB;
objB.a = objA;
$.extend(true,{},a);
//这个时候就出现异常了
//Uncaught RangeError: Maximum call stack size exceeded(…)
That is,
$.extend()
in
jQuery
does not address the problem of circular references.
Using the
parse
and
stringify
methods of
JSON
global objects for deep replication is also a simple and tricky approach.
function jsonClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
var clone = jsonClone({ a:1 });
However, there are hidden pits that can be used in this way, and the only objects it can handle correctly are
Number
String
Boolean
Array
flat objects, which are data structures that can be represented directly by
json
Let's give you a simple solution that, of course, is implemented by reference to someone else's way. I hope it will be useful to all of you.
var clone = (function() {
//这个方法用来获取对象的类型 返回值为字符串类型 "Object RegExp Date Array..."
var classof = function(o) {
if (o === null) {
return "null";
}
if (o === undefined) {
return "undefined";
}
// 这里的Object.prototype.toString很可能用的就是Object.prototype.constructor.name
// 这里使用Object.prototype.toString来生成类型字符串
var className = Object.prototype.toString.call(o).slice(8, -1);
return className;
};
//这里这个变量我们用来存储已经保存过的属性,目的在于处理循环引用的问题
var references = null;
//遇到不同类型的对象的处理方式
var handlers = {
//正则表达式的处理
'RegExp': function(reg) {
var flags = '';
flags += reg.global ? 'g' : '';
flags += reg.multiline ? 'm' : '';
flags += reg.ignoreCase ? 'i' : '';
return new RegExp(reg.source, flags);
},
//时间对象处理
'Date': function(date) {
return new Date(+date);
},
//数组处理 第二个参数为是否做浅拷贝
'Array': function(arr, shallow) {
var newArr = [],
i;
for (i = 0; i < arr.length; i++) {
if (shallow) {
newArr[i] = arr[i];
} else {
//这里我们通过reference数组来处理循环引用问题
if (references.indexOf(arr[i]) !== -1) {
continue;
}
var handler = handlers[classof(arr[i])];
if (handler) {
references.push(arr[i]);
newArr[i] = handler(arr[i], false);
} else {
newArr[i] = arr[i];
}
}
}
return newArr;
},
//正常对象的处理 第二个参数为是否做浅拷贝
'Object': function(obj, shallow) {
var newObj = {}, prop, handler;
for (prop in obj) {
//关于原型中属性的处理太过复杂,我们这里暂时不做处理
//所以只对对象本身的属性做拷贝
if (obj.hasOwnProperty(prop)) {
if (shallow) {
newObj[prop] = obj[prop];
} else {
//这里还是处理循环引用的问题
if (references.indexOf(obj[prop]) !== -1) {
continue;
}
handler = handlers[classof(obj[prop])];
//如果没有对应的处理方式,那么就直接复制
if (handler) {
references.push(obj[prop]);
newObj[prop] = handler(obj[prop], false);
} else {
newObj[prop] = obj[prop];
}
}
}
}
return newObj;
}
};
return function(obj, shallow) {
//首先重置我们用来处理循环引用的这个变量
references = [];
//我们默认处理为浅拷贝
shallow = shallow === undefined ? true : false;
var handler = handlers[classof(obj)];
return handler ? handler(obj, shallow) : obj;
};
}());
(function() {
//下面是一些测试代码
var date = new Date();
var reg = /hello word/gi;
var obj = {
prop: 'this ia a string',
arr: [1, 2, 3],
o: {
wow: 'aha'
}
};
var refer1 = {
arr: [1, 2, 3]
};
var refer2 = {
refer: refer1
};
refer1.refer = refer2;
var cloneDate = clone(date, false);
var cloneReg = clone(reg, false);
var cloneObj = clone(obj, false);
alert((date !== cloneDate) && (date.valueOf() === cloneDate.valueOf()));
alert((cloneReg !== reg) && (reg.toString() === cloneReg.toString()));
alert((obj !== cloneObj) && (obj.arr !== cloneObj.arr) && (obj.o !== cloneObj.o) && (JSON.stringify(obj) === JSON.stringify(cloneObj)));
clone(refer2, false);
alert("I'm not dead yet!");
// Output:
// true
// true
// true
// I'm not dead yet!
}());
The above is about
JavaScript
copy of some knowledge, I hope to help you, interested in
JavaScript
students can look at the tutorial
JavaScript tutorial: https://www.w3cschool.cn/javascript/
JavaScript Microsyscope: https://www.w3cschool.cn/minicourse/play/jscourse