이 글의 내용은 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
Number
、String
、Boolean,
而对象就是像这样的东西{ name: 'Larry', skill: 'Node.js' },对象跟基本类型最大的不同就在于他们的传值方式。基本类型是按值传递,像是这样:在修改a
时并不会改到b
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 }
但对象就不同,对象传的是按引用传值:
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 }
复制一份obj1
叫做obj2,
然后把obj2.b
改成100,
但却不小心改到obj1.b,
因为他们根本是同一个对象,这就是所谓的浅拷贝。
要避免这样的错误发生就要写成这样:
<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>
这样就是深拷贝,不会改到原本的obj1。
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
也就是简单地复制而已
1、简单地复制语句
Object.assign(target, ...sources)
结果为:
2、Object.assign()
<code>Object.assign
是ES6的新函数。Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 <code>Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
var obj = { a: {a: "hello", b: 21} }; var initalObj = Object.assign({}, obj); initalObj.a.a = "changed"; console.log(obj.a.a); // "changed"
参数:
target:目标对象。
sources:任意多个源对象。
返回值:目标对象会被返回。
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 }
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 }
要完全复制又不能修改到原对象,这时候就要用 Deep Copy,这里会介绍几种Deep Copy 的方式。
1、手动复制
把一个对象的属性复制给另一个对象的属性
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
但这样很麻烦,要一个一个自己复制;而且这样的本质也不能算是 Deep Copy,因为对象里面也可能回事对象,如像下面这个状况:
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
虽然obj1
跟obj2
是不同对象,但他们会共享同一个obj1.body
,
所以修改obj2.body.a
时也会修改到旧的。
2、对象只有一层的话可以使用上面的:Object<span class="token punctuation">.<span class="token function">assign()函数</span></span>
<code>Object.assign({}, obj1)的意思是先建立一个空对象{},接着把obj1
中所有的属性复制过去,所以obj2
会长得跟obj1
一样,这时候再修改obj2.b
也不会影响obj1。
因为<code>Object.assign跟我们手动复制的效果相同,所以一样只能处理深度只有一层的对象,没办法做到真正的 Deep Copy。不过如果要复制的对象只有一层的话可以考虑使用它。
3、转成 JSON 再转回来
用JSON.stringify
把对象转成字符串,再用JSON.parse
把字符串转成新的对象。
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' <-- 没复制
这样做是真正的Deep Copy,这种方法简单易用。
但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。
这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象
,即那些能够被 json 直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。
也就是说,只有可以转成JSON
格式的对象才可以这样用,像function
没办法转成JSON。
하지만 객체가 다릅니다. 값으로 전달 값으로 전달:
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);
obj1
의 복사본을 복사하고 obj2,
라고 명명한 다음 obj2.b
를 로 변경합니다. >100,
>그러나 기본적으로 동일한 객체이기 때문에 실수로 obj1.b,
로 변경했는데, 이것이 소위 얕은 복사본입니다. 🎜🎜이러한 오류를 방지하려면 다음과 같이 작성하세요. 🎜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);
obj1로 변경되지 않습니다.
🎜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; }
<code>Object.sign
은 ES6의 새로운 함수입니다. Object.sign() 메서드는 소스 객체의 열거 가능한 속성을 원하는 만큼 대상 객체에 복사한 다음 대상 객체를 반환할 수 있습니다. 그러나 <code>Object.sign()은 단순 복사를 수행합니다. 복사되는 것은 객체 자체가 아닌 객체의 속성에 대한 참조입니다. 🎜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
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
obj1
및 obj2
는 서로 다른 객체이지만 공유합니다. 동일한 obj1.body
,
따라서 obj2.body.a
를 수정하면 이전 항목도 수정됩니다. 🎜🎜2. 객체에 레이어가 하나만 있는 경우 위의 Object<span class="token tempotion">.<span class="token function"을 사용할 수 있습니다. >sign() 함수 🎜🎜
🎜🎜<code>Object.sign({}, obj1)은 먼저 빈 객체를 생성하는 것을 의미합니다. }, obj1
의 모든 속성을 복사합니다. 그러면 obj2
는 obj1
과 동일하게 보입니다. 그런 다음 obj2.b<를 수정합니다. /code>는 <code>obj1에도 영향을 주지 않습니다.
JSON.stringify
변환 사용 객체를 문자열로 변환한 다음 JSON.parse
를 사용하여 문자열을 새 객체로 변환합니다. 🎜🎜rrreee🎜이것은 실제 Deep Copy입니다. 이 방법은 간단하고 사용하기 쉽습니다. 🎜Number, String, Boolean, Array, flat 개체
, 즉 json으로 직접 표현할 수 있는 데이터 구조뿐입니다. RegExp 개체는 이런 방식으로 전체 복사될 수 없습니다. 🎜🎜즉, JSON
형식으로 변환할 수 있는 개체만 이 방법으로 사용할 수 있습니다. 예를 들어 function
은 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
这个性能还不错,使用起来也很简单。
参考:
위 내용은 JavaScript의 얕은 복사와 깊은 복사에 대한 심층적인 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!