这篇文章介绍的内容是关于深入了解javascript中的浅拷贝和深拷贝 ,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下
在 JS 中有一些基本类型像是Number、String、Boolean,而对象就是像这样的东西{ name: 'Larry', skill: 'Node.js' },对象跟基本类型最大的不同就在于他们的传值方式。
基本类型是按值传递,像是这样:在修改a时并不会改到b
var a = 25; var b = a; b = 18; console.log(a);//25 console.log(b);//18
但对象就不同,对象传的是按引用传值:
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = obj1;
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 100, c: 30 } <p>复制一份<code>obj1</code>叫做<code>obj2,</code>然后把<code>obj2.b</code>改成<code>100,</code>但却不小心改到<code>obj1.b,</code>因为他们根本是同一个对象,这就是所谓的浅拷贝。</p><p>要避免这样的错误发生就要写成这样:</p><pre class="brush:php;toolbar:false;">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 } <p><br></p><p>这样就是深拷贝,不会改到原本的<code>obj1。</code></p><h2><strong>浅拷贝(Shallow Copy) VS 深拷贝(Deep Copy)</strong></h2><p><img src="https://img.php.cn/upload/article/000/099/200/4ba857fabcd595bc7f4ced30f40d649c-0.png?x-oss-process=image/resize,p_40" alt=""></p><p>浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。</p><h2>浅拷贝的实现方式</h2><p>也就是简单地复制而已</p><p><strong>1、简单地复制语句</strong></p><pre class="brush:php;toolbar:false;"> <script>
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>
结果为:

2、Object.assign()
方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign是ES6的新函数。Object.assign()Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
Object.assign(target, ...sources)
参数:
target:目标对象。
sources:任意多个源对象。
返回值:目标对象会被返回。
var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"
兼容性:

需要注意的是:
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 } <p><br></p><h2>深拷贝的实现方式</h2><p>要完全复制又不能修改到原对象,这时候就要用 Deep Copy,这里会介绍几种Deep Copy 的方式。</p><p><strong>1、手动复制</strong></p><p>把一个对象的属性复制给另一个对象的属性</p><pre class="brush:php;toolbar:false;">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 } <p>但这样很麻烦,要一个一个自己复制;而且这样的本质也不能算是 Deep Copy,因为对象里面也可能回事对象,如像下面这个状况:</p><pre class="brush:php;toolbar:false;">var obj1 = { body: { a: 10 } };
var obj2 = { body: obj1.body };
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 20 } } <p>虽然<code>obj1</code>跟<code>obj2</code>是不同对象,但他们会共享同一个<code>obj1.body</code><code>,</code>所以修改<code>obj2.body.a</code>时也会修改到旧的。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill5980" title="javascript-pro"><img
src="https://img.php.cn/upload/skill/000/000/081/179074807567238.jpg" alt="javascript-pro" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill5980" title="javascript-pro" class="overflowclass">javascript-pro</a>
<p class="overflowclass">专注现代 ECMAScript、异步编程、性能优化和全栈的 JavaScript 专家,适用于现代开发</p>
</div>
<a rel="nofollow" href="/xiazai/skill5980" title="javascript-pro" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><p><strong>2、对象只有一层的话可以使用上面的:<code class=" js">Object<span class="token punctuation">.<span class="token function">assign()函数</span></span></code></strong></p><p><span class="token punctuation"><code>Object.assign({}, obj1)</code>的意思是先建立一个空对象{},接着把<code>obj1</code>中所有的属性复制过去,所以<code>obj2</code>会长得跟<code>obj1</code>一样,这时候再修改<code>obj2.b</code>也不会影响<code>obj1。</code><br><br>因为<code>Object.assign</code>跟我们手动复制的效果相同,所以一样只能处理深度只有一层的对象,没办法做到真正的 Deep Copy。不过如果要复制的对象只有一层的话可以考虑使用它。</span></p><p><strong><span class="token punctuation">3、</span>转成 JSON 再转回来</strong></p><p><span class="token punctuation">用<code>JSON.stringify</code>把对象转成字符串,再用<code>JSON.parse</code>把字符串转成新的对象。</span></p><pre class="brush:php;toolbar:false;">var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } <p>这样做是真正的Deep Copy,<span style="line-height: 1.5;">这种方法简单易用。</span><br></p><p>但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。</p><p>这种方法能正确处理的对象只有 <code>Number, String, Boolean, Array, 扁平对象</code>,即那些能够被 json 直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。</p><p>也就是说,只有可以转成<code>JSON</code>格式的对象才可以这样用,像<code>function</code>没办法转成<code>JSON。</code></p><pre class="brush:php;toolbar:false;">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' <p>要复制的<code>function</code>会直接消失,所以这个方法只能用在单纯只有数据的对象。</p><p><strong>4、递归拷贝</strong></p><pre class="brush:php;toolbar:false;">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
这个性能还不错,使用起来也很简单。
参考:
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










