whatisthis?

javaScript. (22) 객체의 복사 본문

WEB STUDY/JAVASCRIPT

javaScript. (22) 객체의 복사

thisisyjin 2022. 1. 30. 11:48

자바스크립트에서는 일반 String / Number / Boolean 값을

다음과 같이 복사(copy)할 수 있다.

const string = 'hello';
const copy = string;

copy;      // 'hello'

 

 

function sayHello() {
    console.log('hello');
}

const copyFunc = sayHello;

물론 함수도 copy할 수 있다!

 

 

자바스크립트에서는 null과 undefined 를 제외한 모든 타입이 객체이므로

'객체' = 함수, 배열, 일반 객체도 복사할 수 있다.

 

❕ 단, 복사된 값을 조작할 때 차이가 있다.

 

 

let string = 'hello';
let copy = string;

copy = 'bye';   // 복사본의 값을 바꿔줌

string;    // 'hello' ㅡ 원본은 변화X

위 예제에서 복사본인 copy의 값을 'bye'로 바꿔보았다.

copy의 값이 바뀌어도, 원본 string의 값은 변화가 없다.

 

 

그렇다면, 객체의 경우엔 어떨까? (배열 / 일반객체 / 함수)

❗ 참고 : 상속이 없는 '일반객체' 만 해당함.

 

let array = ['a', 'b,' 'c'];
let copyArr = array;          // 배열(객체) 복사 
copyArr[0] = 'change';        // 복사본 변경

console.log(array);       // ['change', 'b', 'c'] ㅡ 원본배열이 바뀜

 

객체의 경우에는,

복사본을 변경해도 원본까지 바뀌게 된다.

 

copyArr 변수에 array 배열을 대입하여 복사했는데,

 

이때 '객체'의 특징으로

다른 변수에 대입할 때 값을 복사하는게 아니라 참조(메모리 주소)를 복사한다.

>> 문자열 / 숫자 / 불린 제외한 객체 모두.

 

변수(variable)은 모두 메모리에 저장된다.

그리고 대입을 하면 변수의 이름은 저장된 메모리의 주소를 가리킨다.

>> 즉, 대입을 통해 복사한다면 값은 하나인데 변수는 여러개인 것( = 주소가 여러개이므로 )

 

이것을 참조(Reference) 라고 한다.

 

위에서 copyArr 변수가 바뀌면 array값이 바뀌는 것이 바로 이 '참조' 때문이다.
ㅡ copyArr 변수는 array변수를 참조하고 있어서, 값은 하나지만 주소만 다르므로
값이 변경되면 원본의 값도 바뀌게 된다.

 

 

참조 현상이 일어나지 않게 하려면

메모리에 ['a', 'b', 'c']를 두개 만들어야 한다.

(즉, 값이 하나가 아니라 두개가 되도록 따로따로 운영)

 

객체는 대입(=)시 기본적으로 값(value)이 아닌 주소(reference)를 복사하므로

임의로 조작을 해야한다.

let array =  ['a', 'b', 'c'];
let copy = Array.prototype.slice.call(array);
copy[0] = 'change';

console.log(array);     // ['a', 'b', 'c']

위 예제에서는

1) Array.prototype.slice로 배열을 그대로 반환하도록 하였다.

 

- array.slice(0)과 같으므로 0개만큼 자르는 것과 같다 = 즉, 원본 배열 그대로 반환함

 

- array.slice(0)을 해도 좋지만, argument같은 것을 복사하려면 Array.prototype.slice를 한 후 call을 하는것이 좋음.

>> 배열이 아니라 유사배열일 때는 배열의 메서드를 못 쓰므로

 

2) call()을 통해 this값을 array로 설정해주고 호출한다.

 


복사(Copy)

 

📌 얕은복사(shallow copy)

- 가장 상위 객체만 새로 생성되고, 내부 객체들은 참조관계인 경우.

 

📌 깊은복사(deep copy)

- 내부 객체까지 모두 새로 생성된 것.

 

 

 

** 위 예제는 내부 객체가 없어서 shallow인지 deep인지 정하기 애매함.

 

 

1 / 얕은 복사 (shallow copy) - 예제

let array = [{ name: 'a' }, { name: 'b' }, { name: 'c' }];     // 객체(배열) 안에 내부객체 존재

let shallow = Array.prototype.slice.call(array);

shallow[0].name = 'd';    // 내부 객체의 값은 바뀜 (name)
shallow[1] = 'e';       // 내부 객체 자체는 바뀌지 X
console.log(array); // [{ name: 'd' }, { name: 'b' }, { name: 'c' }]

위 예제에서는 shallow[0].name은 array(원본)에 적용되지만(즉, 값이 바뀌지만)

shallow[1]은 적용되지 않는다. (내부객체 그 자체는 안바뀜)

>> 얕은 복사 (shallow copy)

 

** 가장 상위 객체에 직접 변경하는 것은 적용되지 않지만,

내부 객체들은 참조(reference)로 아직 이어져있어서 바뀌는 것임.

 

즉, 가장 바깥 껍데기만 복사됨. (안에 내부까지는 X)

 

 

 

 

2 / 깊은 복사 (deep copy) - 예제

function copyObj(obj) {
  let copy = {};
  if (Array.isArray(obj)) {                  // 1️⃣ obj가 배열이면
    copy = obj.slice().map((v) => {          // copy에 원본 배열 복사 ㅡ slice 이용 + map
      return copyObj(v);
    });
  } else if (typeof obj === 'object' && obj !== null) {      // 2️⃣ (null이 아닌) 객체면 (typeof 오류로 인해 null이 object로 뜸)
    for (let attr in obj) {                    // for ... in문으로 obj 안에 속성들
      if (obj.hasOwnProperty(attr)) {          // 자기자신의 속성이면 (상속이 아니면)
        copy[attr] = copyObj(obj[attr]);       // 객체 복사 ㅡ 각 속성들을 객체 속성으로 저장
      }
    }
  } else {
    copy = obj;             // 3️⃣ 일반형이면 (Number, String, Boolean)
  } 
  return copy;
}

let obj = { a: 1, b: 2, c: [{ d: null, e: 'f' }] };      // obj 선언
let obj2 = copyObj(obj);        // 함수 리턴값 ㅡ copy 저장

obj2.a = 3;           // 🎲 값 변경해도
obj2.c[0].d = true;

console.log(obj.a); // 1        //  ❌ 안바뀐다. ㅡ 내부 객체의 값도 적용 X
console.log(obj.c[0].d); // null

 

 

💡 for ... in

 

- obj 안의 키(key)를 순서대로 반복한다.

- 만약, 키가 숫자라면 순서대로 반복되지 X. (즉, 문자열인 키에만 사용할 것)

 

for...in - JavaScript | MDN

for...in문은 상속된 열거 가능한 속성들을 포함하여 객체에서 문자열로 키가 지정된 모든 열거 가능한 속성에 대해 반복합니다. (Symbol로 키가 지정된 속성은 무시합니다.)

developer.mozilla.org

 

 

이때, for ... in을 돌리면 자신의 속성이 아닌 [[prototype]]에 존재하는 상속된 객체의 속성도 반복되므로

if (obj.hasOwnProperty(attr))를 통과한 obj 자신의 속성만이 복사되도록 하는 것.

 

 

 

 

+) 함수의 복사

 

- 함수는 복사시 bind를 하면 된다.

- this를 기존 함수와 같게 하면 함수가 똑같이 복사된다.

 

let func = function () {
  alert('hi');
};
func2 = func.bind(this);
func2(); // 'hi'

REFERENCE

 

https://www.zerocho.com/category/JavaScript/post/5750d384b73ae5152792188d

 

(JavaScript) 객체의 복사

안녕하세요, 이번 시간에는 객체의 복사에 대해서 알아보겠습니다. 일반 문자열, 숫자, 불린 같은 경우는 var string = 'hello';var copy = string;console.log(copy); // 'hello' 하면 바로 복사가 되는데요. 객체(

www.zerocho.com

이 포스팅은 zerocho님의 javascript 강의를 보고 작성한 글입니다.

공부+기록 용으로 작성한 것이며, 자세한 것은 위 포스팅을 참고하세요!