JavaScript 기본 심법 - 깊이 복사

원문 주소: JavaScript 기본 심법 - 깊이 복사
환영 스타.
잘못된 점 이 있 으 면 지적 을 환영 합 니 다.
얕 은 복사 와 깊 은 복사 모두 JS 의 인용 유형 에 있어 얕 은 복사 본 은 복사 대상 의 인용 일 뿐 복사 후의 대상 이 변화 하면 원래 의 대상 도 변화 할 수 있다.깊 은 복사 만 이 진정 으로 대상 에 대한 복사 이다.
머리말
깊이 복사 하면 먼저 JavaScript 의 데이터 형식 을 언급 해 야 합 니 다. 이전의 글 은 JavaScript 기초 심법 인 데이터 형식 을 분명하게 말 했 습 니 다. 여 기 는 더 이상 말 하지 않 겠 습 니 다.
알 아야 할 것 은 자바 스 크 립 트 의 데이터 형식 은 기본 데이터 형식 과 참조 데이터 형식 으로 나 뉜 다 는 것 이다.
기본 데이터 형식의 복사 에 있어 깊이 복사 의 차이 가 없다. 우리 가 말 하 는 깊이 복사 는 모두 데이터 형식 을 인용 하 는 데 있어 서 이다.
얕 은 복사
얕 은 복사 란 인용 만 복사 하고 진짜 값 은 복사 하지 않 는 다 는 뜻 이다.
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'}}

위의 코드 는 가장 간단 한 이용 = 할당 연산 자 를 이용 하여 얕 은 복사 본 을 실현 하 였 으 며, cloneArraycloneObj 의 변화 에 따라 originArrayoriginObj 도 이에 따라 변화 하 였 음 을 뚜렷하게 볼 수 있다.
딥 카피
깊 은 복사 란 목표 에 대한 완전한 복사 이다. 얕 은 복사 처럼 인용 만 복사 한 것 이 아니 라 값 도 복사 한 것 이다.
깊 은 복사 만 하면 그들 은 죽어도 서로 왕래 하지 않 고 누구 에 게 도 영향 을 주지 않 는 다.
현재 심 복 사 를 실현 하 는 방법 은 많 지 않 으 며 주로 두 가지 이다.
  • 이용 JSON 대상 중 parsestringify
  • 재 귀 를 이용 하여 각 층 이 대상 을 다시 만 들 고 값 을 부여 합 니 다
  • 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 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'}};

    깊 게 복사 하 는 것 도 편 하고.그러나 이 방법 은 간단 한 상황 에 만 적용 된다.예 를 들 어 아래 와 같은 대상 은 적용 되 지 않 는 다.
    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 에서 원인 을 찾 았 다.
    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). undefined, function, symbol 전환 과정 에서 무시 된다.
    아 시 겠 죠? 대상 에 함수 가 하나 있 을 때 (흔 합 니 다) 이 방법 으로 깊이 복사 할 수 없습니다.
    재 귀적 방법
    재 귀적 인 사상 은 매우 간단 하 다. 즉, 모든 층 의 데이터 에 대해 한 번 -> 의 조작 을 실현 하고 간단 하고 거 친 코드 를 사용 하 는 것 이다.
    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 에서 수조 에 두 가지 방법 concatslice 이 원수 조 에 대한 복 사 를 실현 할 수 있다 는 것 을 알 고 있다. 이 두 가지 방법 은 원수 조 를 수정 하지 않 고 수 정 된 새로운 수조 로 되돌아간다.
    아울러 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 배열 의 1 층 을 깊이 복사 할 뿐이다.
    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 배열 의 1 층 을 깊이 복사 할 뿐이다.
    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}}

    결론: ... 대상 1 층 의 깊 은 복사 가 이 루어 졌 다.뒤에 있 는 것 은 복사 한 인용 값 일 뿐 입 니 다.
    첫 번 째 얕 은 복사
    우 리 는 목표 대상 의 1 층 을 깊이 복사 한 다음 에 뒤의 것 은 얕 은 복사 로 '1 층 얕 은 복사' 라 고 할 수 있다 는 것 을 알 게 되 었 다.
    우 리 는 스스로 이런 함 수 를 실현 할 수 있다.
    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 === originObjfalse 라 는 것 을 검증 했다. 이 두 대상 의 인용 주소 가 다르다 는 것 을 설명 한다. 그것 은 바로 수정 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 중의 한 대상 이 수정 되 었 다.깊 게 복사 하기 로 했 는데?인용 주소 가 다 르 잖 아 요.
    그 렇 군요.
  • shallowClone 의 코드 에서 알 수 있 듯 이 우 리 는 1 층 의 목표 만 실 시 했 고 2 층 에서 시 작 된 목 표 는 = 할당 연산 자 를 직접 이용 하여 복사 한 것 이다.
  • so, 2 층 후의 목 표 는 모두 하나의 인용, 즉 얕 은 복사 일 뿐이다.

  • 총결산
  • 할당 연산 자 = 는 얕 은 복사 로 대상 의 인용 값 만 복사 합 니 다.
  • JavaScript 에서 배열 과 대상 이 자체 적 으로 가지 고 있 는 복사 방법 은 모두 '첫 번 째 얕 은 복사' 이다.
  • JSON.stringify 은 깊 은 복사 본 을 실현 하지만 목표 대상 에 대한 요구 가 있다.
  • 진정한 의미 의 깊 은 복사 하려 면 재 귀적 하 세 요.
  • 좋은 웹페이지 즐겨찾기