primitive type & reference type

holang-i·2023년 3월 2일
0

JavaScript에서 자료형(type)이란 값(value)의 종류를 말하는데 각각의 자료형에는 고유한 속성과 메서드를 가지고 있다.
자료형은 크게 두 가지로 구분할 수 있는데 원시 자료형(primitive type)과 참조 자료형(reference type)이 있다.


원시 자료형(primitive type)

  • string
  • number
  • boolean
  • undefined
  • null
  • symbol

참조 자료형(reference type)

원시 자료형이 아닌 모든 자료형은 참조 자료형으로 여러 데이터를 다룰 수 있는 배열, 객체가 대표적인 참조 자료형이고, 함수도 참조 자료형으로 분류된다.


원시 자료형과 참조 자료형의 특징

primitive typereference type
원시 자료형을 변수에 할당하면 메모리 공간에 값 자체가 저장참조 자료형을 변수에 할당하면 메모리 공간에 주솟값이 저장
원시 값을 갖는 변수를 다른 변수에 할당하면 원시 값 자체가 복사되어 전달참조 값을 갖는 변수를 다른 변수에 할당하면 주솟값이 복사되어 전달
원시 자료형은 변경 불가능한 값(immutable value),
한 번 생성된 원시 자료형은 읽기 전용(read only) 값
참조 자료형은 변경이 가능한 값(mutable value)

값 자체를 복사

원시 자료형을 할당한 변수를 다른 변수에 할당하면 값 자체의 복사가 일어난다. -> 값 자체가 복사되는 것은 둘 중 하나의 값을 변경해도 다른 하나에는 영향을 미치지 않는다는 것을 의미한다.

let word = 'hello';
let copyWord = word;

console.log(word); // hello
console.log(copyWord); // hello
console.log(word === copyWord); // true

word = 'javascript';
console.log(word); // javascript
console.log(copyWord); // hello
console.log(word === copyWord); // false

메모리에 저장되어있는 주소를 복사

참조 자료형은 임의의 저장공간에 값을 저장하고, 그 저장공간을 참조하는 주소를 메모리에 저장한다. 그렇기 때문에 다른 변수에 할당할 경우 값 자체가 아닌 메모리에 저장되어 있는 주소가 복사된다.

let arr = [1, 2, 3, 4, 5];
let copyArr = arr;

console.log(arr); // (5) [1, 2, 3, 4, 5]
console.log(copyArr); // (5) [1, 2, 3, 4, 5]
console.log(arr === copyArr); // true


둘 중 하나를 변경했을 때 해당 변수가 참조하고 있는 주소에 있는 값이 변경되기 때문에 다른 하나에도 영향을 미친다.
-> 참조 자료형이 저장된 변수를 다른 변수에 할당할 경우, 두 변수는 같은 주소를 참조하고 있을 뿐, 값 자체가 복사되었다고는 볼 수 없다.


배열, 객체와 같은 참조 자료형을 복사하여 똑같은 요소와 프로퍼티를 가지지만 원본과 복사본이 서로 영향을 미치지 않도록 하는 방법에 대해 살펴볼 것이다.

배열 복사하기

배열을 복사하는 방법에는 크게 두 가지가 있다.
slice()를 사용하는 방법 spread를 사용하는 방법

얕은 복사(Shallow Copy)

slice( )

배열 내장 메서드인 slice()를 사용하여 원본 배열을 복사할 수 있다.
새롭게 생성된 copyArr 배열은 원본 배열과 같은 요소를 가지고있지만 참조하고 있는 주소는 다르다.

let arr = [1, 2, 3];
let copyArr = arr;
console.log(arr); // [1, 2, 3]
console.log(copyArr); // [1, 2, 3]
console.log(arr === copyArr); // true


spread syntax

spread syntax는 ES6에서 추가된 문법으로 변수명 앞에 ... 을 붙여서 사용할 수 있다.

let arr = [1, 2, 3];
console.log(...arr); // 1 2 3

같은 요소를 가진 배열을 각각 만든 변수에 할당했을 때 두 변수는 참조 자료형이기 때문에 서로 다른 주소를 참조한다.

let arr1 = [1, 2, 3];
let arr2 = [1, 2, 3];
            
console.log(arr1 === arr2); // false

spread를 사용하면 어떨까? 새로운 배열 안에 원본 배열을 펼쳐서 전달하면 원본 배열과는 같은 요소를 갖고 있지만, 각각 다른 주소를 참조하게 되기 때문에 결과적으로 slice() 메서드를 사용한 것과 동일하게 작동한다.

let arr = [1, 2, 3];
let spreadArr = [...arr];

console.log(arr); // (3) [1, 2, 3]
console.log(spreadArr); // (3) [1, 2, 3]
console.log(arr === spreadArr); // false

spreadArr.push(100);
console.log(arr); // (3) [1, 2, 3]
console.log(spreadArr); // (4) [1, 2, 3, 100]

객체 복사하기

얕은 복사(Shallow Copy)

Object.assign()

객체를 복사하기 위해서는 Object.assign()을 사용한다.

let obj = { name: "hoho", age: 20 };
let copyObj = Object.assign({}, obj);

console.log(copyObj) // { name: "hoho", age: 20 }
console.log(obj === copyObj) // false

spread syntax

spread syntax는 배열뿐만 아니라 객체를 복사할 때도 사용 가능하다.

let obj = { name: "hoho", age: 20 };
let copyObj = {...obj};

console.log(copyObj) // { name: "hoho", age: 20 }
console.log(obj === copyObj) // false

객체를 복사할 때 예외사항

참조 자료형 내부에 참조 자료형이 중첩된 경우 slice(), Object.assign(), spread syntax를 사용해도 참조 자료형 내부에 참조 자료형이 중첩된 구조는 복사할 수 없다.
딱 1단계까지만 복사가 가능하기 때문에 깊은 복사는 불가능하다.

깊은 복사(Deep Copy)

참조 자료형 내부에 중첩되어 있는 모든 참조 자료형을 복사하는 것을 깊은 복사라고 하는데 JavaScript의 다른 문법을 응용하여 깊은 복사와 같은 결과물을 만들어 낼 수 있다.

JSON.stringify() + JSON.parse() 사용하기

  • JSON.stringify()는 참조 자료형을 문자열 형태로 변환하여 반환
  • JSON.parse()는 문자열의 형태를 객체로 변환하여 반환

위의 두 가지를 사용하여 깊은 복사와 같은 결과물을 만들어 낼 수 있다.

  1. 중첩된 참조 자료형을 JSON.stringify()를 사용하여 문자열의 형태로 변환한다.
  2. 반환된 값에 다시 JSON.parse()를 사용하여 깊은 복사와 같은 결과물을 반환한다.
const obj = {
  name: 'hoho',
  age: 20,
  arr: [1, 2, [3, 4, 5]]
}

const copyObj = JSON.parse(JSON.stringify(obj));

console.log(obj); // {name: 'hoho', age: 20, arr: Array(3)}
console.log(copyObj); // {name: 'hoho', age: 20, arr: Array(3)}
console.log(obj === copyObj); // false
console.log(obj.arr[2] === copyObj[2]); // false

JSON.stringify() + JSON.parse()를 사용했을 때 깊은 복사가 되지 않는 예외

중첩된 참조 자료형 중에 함수가 포함되어 있을 경우, JSON.stringify() + JSON.parse()를 사용했을 때 함수가 null로 바뀌는 문제가 발생한다.
그래서 이 방법또한 완벽하게 깊은 복사 방법이 될 수 없다.

const arr = [1, 2, [3, function(){ console.log('hello')}]];
const copyArr = JSON.parse(JSON.stringify(arr));

console.log(arr); // [1, 2, [3, function(){ console.log('hello')}]]
console.log(copyArr); // [1, 2, [3, null]]
console.log(arr === copyArr); // false
console.log(arr[2] === copyArr[2]); // false

외부 라이브러리 사용

완전하게 깊은 복사를 해야 되는 경우 lodash, ramda 외부 라이브러리를 통해 깊은 복사를 이용할 수 있다고 한다.

profile
🌿 주니어 프론트엔드 개발자입니다! 부족하거나 잘못된 정보가 있다면 알려주세요:)

0개의 댓글