The content of this article is about in-depth understanding of shallow copy and deep copy in JavaScript. It has certain reference value. Now I share it with you. Friends in need can refer to it
There are some basic types in JS like Number
, String
, Boolean,
And objects are things like this {name: 'Larry', skill : 'Node.js' }, the biggest difference between objects and basic types lies in the way they pass values.
The basic type is passed by value, like this: When modifying a
, it will not be changed to b
var a = 25; var b = a; b = 18; console.log(a);//25 console.log(b);//18
, but the object is different, the object What is passed is passing by reference:
var obj1 = { a: 10, b: 20, c: 30 }; var obj2 = obj1; obj2.b = 100; console.log(obj1); // { a: 10, b: 100, c: 30 } <-- b 被改到了 console.log(obj2); // { a: 10, b: 100, c: 30 }
Copy a copy of obj1
and call it obj2,
then change obj2.b
to 100 ,
But accidentally changed to obj1.b,
Because they are basically the same object, this is the so-called shallow copy.
To avoid such errors, write like this:
var obj1 = { a: 10, b: 20, c: 30 }; var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c }; obj2.b = 100; console.log(obj1); // { a: 10, b: 20, c: 30 } <-- b 沒被改到 console.log(obj2); // { a: 10, b: 100, c: 30 }
This is a deep copy and will not change to the original obj1.
Shallow copy only copies points to a certain The pointer to the object does not copy the object itself. The old and new objects still share the same memory. However, deep copy will create an identical object. The new object does not share memory with the original object, and modifications to the new object will not change the original object.
That is, simply copy
1. Simply copy the statement
<script type="text/javascript"> function simpleClone(initalObj) { var obj = {}; for ( var i in initalObj) { obj[i] = initalObj[i]; } return obj; } var obj = { a: "hello", b:{ a: "world", b: 21 }, c:["Bob", "Tom", "Jenny"], d:function() { alert("hello world"); } } var cloneObj = simpleClone(obj); console.log(cloneObj.b); console.log(cloneObj.c); console.log(cloneObj.d); cloneObj.b.a = "changed"; cloneObj.c = [1, 2, 3]; cloneObj.d = function() { alert("changed"); }; console.log(obj.b); console.log(obj.c); console.log(obj.d); </script>
The result is:
2. Object.assign()
<code> Object.assign
is a new function in ES6. The Object.assign() method can copy any number of the source object's own enumerable properties to the target object, and then return the target object. But Object.assign()
performs a shallow copy. What is copied is the reference to the object's attributes, not the object itself.
Object.assign(target, ...sources)
Parameters:
target: target object.
sources: any number of source objects.
Return value: The target object will be returned.
var obj = { a: {a: "hello", b: 21} }; var initalObj = Object.assign({}, obj); initalObj.a.a = "changed"; console.log(obj.a.a); // "changed"
Compatibility:
Note:
Object.assign()可以处理一层的深度拷贝,如下:
var obj1 = { a: 10, b: 20, c: 30 }; var obj2 = Object.assign({}, obj1); obj2.b = 100; console.log(obj1); // { a: 10, b: 20, c: 30 } <-- 沒被改到 console.log(obj2); // { a: 10, b: 100, c: 30 }
If you want to completely copy without modifying the original object, you need to use Deep Copy. Here we will introduce several Deep Copy methods.
1. Manual copy
Copy the properties of one object to the properties of another object
var obj1 = { a: 10, b: 20, c: 30 }; var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c }; obj2.b = 100; console.log(obj1); // { a: 10, b: 20, c: 30 } <-- 沒被改到 console.log(obj2); // { a: 10, b: 100, c: 30 }
But this is very troublesome, you have to do it one by one Copy; and this nature cannot be regarded as Deep Copy, because there may also be objects inside the object, such as the following situation:
var obj1 = { body: { a: 10 } }; var obj2 = { body: obj1.body }; obj2.body.a = 20; console.log(obj1); // { body: { a: 20 } } <-- 被改到了 console.log(obj2); // { body: { a: 20 } } console.log(obj1 === obj2); // false console.log(obj1.body === obj2.body); // true
Although obj1
and obj2
are different objects, but they will share the same obj1.body
,
so when modifying obj2.body.a
, the old one will also be modified.
2. If the object has only one layer, you can use the above: Object<span class="token punctuation">.<span class="token function">assign() function</span></span>
Object.assign({}, obj1)
means to first create an empty object {}, and then copy all the properties in obj1
, so obj2
will look the same as obj1
. Modifying obj2.b
at this time will not affect obj1.
Because Object.assign
has the same effect as our manual copy, it can only process objects with only one layer of depth, and there is no way to achieve true Deep Copy. However, you can consider using it if there is only one layer of objects to be copied.
3. Convert to JSON and then back again
Use JSON.stringify
to convert the object Convert it to a string, and then use JSON.parse
to convert the string into a new object.
var obj1 = { body: { a: 10 } }; var obj2 = JSON.parse(JSON.stringify(obj1)); obj2.body.a = 20; console.log(obj1); // { body: { a: 10 } } <-- 沒被改到 console.log(obj2); // { body: { a: 20 } } console.log(obj1 === obj2); // false console.log(obj1.body === obj2.body); // false
This is true Deep Copy, This method is simple and easy to use.
But this method also has many disadvantages, for example, it will discard the object's constructor. That is to say, after deep copying, no matter what the original constructor of the object is, it will become Object after deep copying.
The only objects that this method can correctly handle are Number, String, Boolean, Array, and flat objects
, that is, those data structures that can be directly represented by json. RegExp objects cannot be deep copied in this way.
That is to say, only objects that can be converted into JSON
format can be used in this way. Things like function
cannot be converted into JSON.
var obj1 = { fun: function(){ console.log(123) } }; var obj2 = JSON.parse(JSON.stringify(obj1)); console.log(typeof obj1.fun); // 'function' console.log(typeof obj2.fun); // 'undefined' <-- 没复制
要复制的function
会直接消失,所以这个方法只能用在单纯只有数据的对象。
4、递归拷贝
function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { if (typeof initalObj[i] === 'object') { obj[i] = (initalObj[i].constructor === Array) ? [] : {}; arguments.callee(initalObj[i], obj[i]); } else { obj[i] = initalObj[i]; } } return obj; }var str = {};var obj = { a: {a: "hello", b: 21} }; deepClone(obj, str); console.log(str.a);
上述代码确实可以实现深拷贝。但是当遇到两个互相引用的对象,会出现死循环的情况。
为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环。
改进版代码如下:
function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况 if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : {}; arguments.callee(prop, obj[i]); } else { obj[i] = prop; } } return obj; }var str = {};var obj = { a: {a: "hello", b: 21} }; deepClone(obj, str); console.log(str.a);
5、使用Object.create()方法
直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。
function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况 if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : Object.create(prop); } else { obj[i] = prop; } } return obj; }
6、jquery
jquery 有提供一个$.extend
可以用来做 Deep Copy。
var $ = require('jquery');var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] };var obj2 = $.extend(true, {}, obj1); console.log(obj1.b.f === obj2.b.f);// false
7、lodash
另外一个很热门的函数库lodash,也有提供_.cloneDeep
用来做 Deep Copy。
var _ = require('lodash');var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] };var obj2 = _.cloneDeep(obj1); console.log(obj1.b.f === obj2.b.f);// false
这个性能还不错,使用起来也很简单。
参考:
The above is the detailed content of An in-depth understanding of shallow copy and deep copy in JavaScript. For more information, please follow other related articles on the PHP Chinese website!