JS의 깊은 복사와 얕은 복사를 사용하는 단계에 대한 자세한 설명

php中世界最好的语言
풀어 주다: 2018-04-12 10:52:34
원래의
1425명이 탐색했습니다.

이번에는 JS의 Deep 및 Shallow Copy를 사용하는 단계에 대해 자세히 설명하겠습니다. JS의 Deep 및 Shallow Copy를 사용할 때 주의 사항은 무엇입니까?

깊은 복사와 얕은 복사에 관해 말하자면, 먼저 JavaScript데이터 유형을 언급해야 합니다. 이전 기사 JavaScript의 기본 - 데이터 유형에서 매우 명확하게 설명했기 때문에 여기에서는 자세히 설명하지 않겠습니다.

한 가지 알아두셔야 할 점은 자바스크립트의 데이터 유형은 기본 데이터 유형과 참조 데이터 유형으로 나누어진다는 것입니다.

기본 데이터 유형의 복사본에는 다크 복사본과 얕은 복사본 사이에 차이가 없습니다. 우리가 다크 복사본과 얕은 복사본이라고 부르는 것은 모두 참조 데이터 형식에 대한 것입니다.

얕은 사본

얕은 복사란 참조만 복사하고 실제 값은 복사하지 않는다는 의미입니다.

rreee

위 코드는 = 대입 연산자를 사용한 얕은 복사본의 가장 간단한 구현입니다. cloneArraycloneObj가 변경되면 originArray 가 변경됩니다. > 및 originObj도 변경되었습니다. cloneArraycloneObj 改变,originArray originObj 也随着发生了变化。

深拷贝

深拷贝就是对目标的完全拷贝,不像浅拷贝那样只是复制了一层引用,就连值也都复制了。

只要进行了深拷贝,它们老死不相往来,谁也不会影响谁。

目前实现深拷贝的方法不多,主要是两种:

  1. 利用 JSON 对象中的 parse 和 stringify

  2. 利用递归来实现每一层都重新创建对象并赋值

JSON.stringify/parse的方法

先看看这两个方法吧:

The JSON.stringify() method converts a JavaScript value to a JSON string.

JSON.stringify 是将一个 JavaScript 值转成一个 JSON 字符串。

The JSON.parse() method parses a JSON string, constructing the JavaScript value or object described by the string.

JSON.parse 是将一个 JSON 字符串转成一个 JavaScript 值或对象。

很好理解吧,就是 JavaScript 值和 JSON 字符串的相互转换。

它能实现深拷贝呢?我们来试试。

const originArray = [1,2,3,4,5];
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneArray = originArray;
const cloneObj = originObj;
console.log(cloneArray); // [1,2,3,4,5]
console.log(originObj); // {a:'a',b:'b',c:Array[3],d:{dd:'dd'}}
cloneArray.push(6);
cloneObj.a = {aa:'aa'};
console.log(cloneArray); // [1,2,3,4,5,6]
console.log(originArray); // [1,2,3,4,5,6]
console.log(cloneObj); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
console.log(originArray); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
로그인 후 복사

确实是深拷贝,也很方便。但是,这个方法只能适用于一些简单的情况。比如下面这样的一个对象就不适用:

const originArray = [1,2,3,4,5];
const cloneArray = JSON.parse(JSON.stringify(originArray));
console.log(cloneArray === originArray); // false
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj === originObj); // false
cloneObj.a = 'aa';
cloneObj.c = [1,1,1];
cloneObj.d.dd = 'doubled';
console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'doubled'}};
console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
로그인 후 복사

发现在 cloneObj 中,有属性丢失了。。。那是为什么呢?

在 MDN 上找到了原因:

If undefined, a function, or a symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array). JSON.stringify can also just return undefined when passing in "pure" values like JSON.stringify(function(){}) or JSON.stringify(undefined).

undefinedfunctionsymbol

딥 카피

Deep Copy는 참조 레이어만 복사하는 Shallow Copy와 달리 대상의 전체 복사본입니다. 깊은 복사본이 만들어지는 한, 그들은 서로 상호 작용하지 않으며 누구도 다른 사람에게 영향을 미치지 않습니다.

현재 딥 카피를 구현하는 방법은 많지 않으며 주로 두 가지 방법이 있습니다:

  1. JSON 개체에서 구문 분석 및 문자열화 활용

  2. 재귀를 사용하여 각 레이어를 재구성합니다객체 생성

    및 값 할당

JSON.stringify/parse 메서드

먼저 이 두 가지 방법을 살펴보겠습니다.

JSON.stringify() 메서드는 JavaScript 값을 JSON 문자열로 변환합니다

🎜. JSON.stringify는 JavaScript 값을 JSON 문자열로 변환합니다. 🎜
🎜 JSON.parse() 메서드는 JSON 문자열을 구문 분석하여 문자열이 설명하는 JavaScript 값 또는 객체를 구성합니다. 🎜
🎜 JSON.parse는 JSON 문자열을 JavaScript 값 또는 객체로 변환합니다. 🎜🎜 이해하기 쉽습니다. JavaScript 값과 JSON 문자열을 변환하는 것입니다. 🎜🎜 딥 카피를 달성할 수 있나요? 한번 해보자. 🎜rreee🎜 실제로 깊은 복사본이며 매우 편리합니다. 그러나 이 방법은 일부 간단한 상황에만 적용할 수 있습니다. 예를 들어 다음 개체는 적용되지 않습니다. 🎜
const originObj = {
 name:'axuebin',
 sayHello:function(){
 console.log('Hello World');
 }
}
console.log(originObj); // {name: "axuebin", sayHello: ƒ}
const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj); // {name: "axuebin"}
로그인 후 복사
🎜 cloneObj 에서 일부 속성이 누락된 것으로 나타났습니다. . . 왜? 🎜🎜 MDN에서 이유를 찾았습니다: 🎜
🎜 정의되지 않은 경우 함수 또는 기호가 발생합니다. 변환은 생략되거나(객체에서 발견될 때) null로 검열됩니다(배열에서 발견되는 경우). JSON.stringify도 가능합니다. 다음과 같은 "순수한" 값을 전달할 때는 정의되지 않은 값을 반환합니다. JSON.stringify(function(){}) 또는 JSON.stringify(정의되지 않음) 🎜
🎜 정의되지 않음, 함수, 기호는 변환 과정에서 무시됩니다. . . 🎜🎜 즉, 객체에 (매우 일반적인) 기능이 포함되어 있으면 이 방법을 사용하여 전체 복사를 수행할 수 없다는 것을 이해하십시오. 🎜🎜 🎜재귀적 방법🎜🎜🎜 재귀의 개념은 매우 간단합니다. 데이터의 각 레이어에 대해 객체 생성 -> 객체 할당 작업을 구현하는 것입니다. 🎜
function deepClone(source){
 const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
 for(let keys in source){ // 遍历目标
 if(source.hasOwnProperty(keys)){
 if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下
 targetObj[keys] = source[keys].constructor === Array ? [] : {};
 targetObj[keys] = deepClone(source[keys]);
 }else{ // 如果不是,就直接赋值
 targetObj[keys] = source[keys];
 }
 } 
 }
 return targetObj;
}
로그인 후 복사
🎜 한번 시도해 보세요: 🎜
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneObj = deepClone(originObj);
console.log(cloneObj === originObj); // false
cloneObj.a = 'aa';
cloneObj.c = [1,1,1];
cloneObj.d.dd = 'doubled';
console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'doubled'}};
console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
로그인 후 복사
🎜 할 수 있다. 그런 다음 기능이 있는 것을 사용해 보세요: 🎜
const originObj = {
 name:'axuebin',
 sayHello:function(){
 console.log('Hello World');
 }
}
console.log(originObj); // {name: "axuebin", sayHello: ƒ}
const cloneObj = deepClone(originObj);
console.log(cloneObj); // {name: "axuebin", sayHello: ƒ}
로그인 후 복사
🎜 그것도 괜찮습니다. 완료. 🎜🎜 이것이 끝이라고 생각하시나요? ? 물론 그렇지 않습니다. 🎜🎜 🎜JavaScript의 복사 방법🎜🎜

我们知道在 JavaScript 中,数组有两个方法 concat 和 slice 是可以实现对原数组的拷贝的,这两个方法都不会修改原数组,而是返回一个修改后的新数组。

同时,ES6 中 引入了 Object.assgn 方法和 ... 展开运算符也能实现对对象的拷贝。

那它们是浅拷贝还是深拷贝呢?

concat

The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.

该方法可以连接两个或者更多的数组,但是它不会修改已存在的数组,而是返回一个新数组。

看着这意思,很像是深拷贝啊,我们来试试:

const originArray = [1,2,3,4,5];
const cloneArray = originArray.concat();
console.log(cloneArray === originArray); // false
cloneArray.push(6); // [1,2,3,4,5,6]
console.log(originArray); [1,2,3,4,5];
로그인 후 복사

看上去是深拷贝的。

我们来考虑一个问题,如果这个对象是多层的,会怎样。

const originArray = [1,[1,2,3],{a:1}];
const cloneArray = originArray.concat();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2; 
console.log(originArray); // [1,[1,2,3,4],{a:2}]
로그인 후 복사

originArray 中含有数组 [1,2,3] 和对象 {a:1},如果我们直接修改数组和对象,不会影响 originArray,但是我们修改数组 [1,2,3] 或对象 {a:1} 时,发现 originArray 也发生了变化。

结论:concat 只是对数组的第一层进行深拷贝。

slice

The slice() method returns a shallow copy of a portion of an array into a new array object selected from begin to end (end not included). The original array will not be modified.

解释中都直接写道是 a shallow copy 了 ~

但是,并不是!

const originArray = [1,2,3,4,5];
const cloneArray = originArray.slice();
console.log(cloneArray === originArray); // false
cloneArray.push(6); // [1,2,3,4,5,6]
console.log(originArray); [1,2,3,4,5];
로그인 후 복사

同样地,我们试试多层的数组。

const originArray = [1,[1,2,3],{a:1}];
const cloneArray = originArray.slice();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2; 
console.log(originArray); // [1,[1,2,3,4],{a:2}]
로그인 후 복사

果然,结果和 concat 是一样的。

结论:slice 只是对数组的第一层进行深拷贝。

Object.assign()

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.

复制复制复制。

那到底是浅拷贝还是深拷贝呢?

自己试试吧。。

结论:Object.assign() 拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。

... 展开运算符

const originArray = [1,2,3,4,5,[6,7,8]];
const originObj = {a:1,b:{bb:1}};
const cloneArray = [...originArray];
cloneArray[0] = 0;
cloneArray[5].push(9);
console.log(originArray); // [1,2,3,4,5,[6,7,8,9]]
const cloneObj = {...originObj};
cloneObj.a = 2;
cloneObj.b.bb = 2;
console.log(originObj); // {a:1,b:{bb:2}}
로그인 후 복사

结论:... 实现的是对象第一层的深拷贝。后面的只是拷贝的引用值。

首层浅拷贝

我们知道了,会有一种情况,就是对目标对象的第一层进行深拷贝,然后后面的是浅拷贝,可以称作“首层浅拷贝”。

我们可以自己实现一个这样的函数:

function shallowClone(source) {
 const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
 for (let keys in source) { // 遍历目标
 if (source.hasOwnProperty(keys)) {
 targetObj[keys] = source[keys];
 }
 }
 return targetObj;
}
로그인 후 복사

我们来测试一下:

const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneObj = shallowClone(originObj);
console.log(cloneObj === originObj); // false
cloneObj.a='aa';
cloneObj.c=[1,1,1];
cloneObj.d.dd='surprise';
로그인 후 복사

经过上面的修改,cloneObj 不用说,肯定是 {a:'aa',b:'b',c:[1,1,1],d:{dd:'surprise'}} 了,那 originObj 呢?刚刚我们验证了 cloneObj === originObj 是 false,说明这两个对象引用地址不同啊,那应该就是修改了 cloneObj 并不影响 originObj。

console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'surprise'}}
console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'surprise'}}
로그인 후 복사

What happend?

originObj 中关于 a、c都没被影响,但是 d 中的一个对象被修改了。。。说好的深拷贝呢?不是引用地址都不一样了吗?

原来是这样:

  1. 从 shallowClone 的代码中我们可以看出,我们只对第一层的目标进行了 深拷贝 ,而第二层开始的目标我们是直接利用 = 赋值操作符进行拷贝的。

  2. so,第二层后的目标都只是复制了一个引用,也就是浅拷贝。

总结

  1. 할당 연산자 = 객체의 참조 값만 복사하는 얕은 복사본을 구현합니다.

  2. JavaScript의 배열 및 객체 복사 방법은 모두 "1단계 얕은 복사본"입니다.

  3. JSON.stringify는 구현된 딥 카피이지만 대상 개체에 대한 요구 사항이 있습니다.

  4. 진정한 딥 카피를 원한다면 재귀를 수행하세요.

이 기사의 사례를 읽으신 후 방법을 마스터하셨다고 생각합니다. 더 흥미로운 정보를 보려면 PHP 중국어 웹사이트의 다른 관련 기사를 주목하세요!

추천 도서:

Vue2.0의 http 요청 사용 및 로딩 표시에 대한 자세한 설명

노드 프로세스 및 child_process 모듈 사용에 대한 자세한 설명

위 내용은 JS의 깊은 복사와 얕은 복사를 사용하는 단계에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿