[자바스크립트] 원시타입(Primitive Type)과 참조타입(Reference Type)
미음제
·2022. 3. 23. 14:44
원시타입과 참조타입
Javascript 언어의 타입은 원시 값과 객체로 나뉜다.
다시 말해서 Javascript의 변수 타입은 원시타입과 참조타입으로 나누어진다.
원시타입의 종류
- Boolean 타입
- Null 타입
- Undefined 타입
- Number 타입
- BigInt 타입
- String 타입
- Symbol 타입
위에 나열된 7개의 타입을 제외한 나머지는 모두 자연스럽게 참조 타입이 된다.
원시타입 변수는 데이터 복사가 발생하면 메모리 공간을 새로 확보해 독립적으로 값을 지정한다.
참조타입은 메모리에 직접적으로 접근하는 것이 아니라 해당 메모리의 주소를 참조하는 타입이다.
원시 값의 불변성
원시타입은 변경이 불가능 값이고, 참조타입은 변경이 가능한 값이다. 변경이 불가능하다는 말은 무엇일까? mdn 문서에 나온 예제를 살펴보자.
// 원시 값
let foo = 5;
// 원시 값을 변경해야 하는 함수 정의
function addTwo(num) {
num += 2;
}
// 같은 작업을 시도하는 다른 함수
function addTwo_v2(foo) {
foo += 2;
}
// 원시 값을 인수로 전달해 첫 번째 함수를 호출
addTwo(foo);
// 현재 원시 값 반환
console.log(foo); // 5
// 두 번째 함수로 다시 시도
addTwo_v2(foo);
console.log(foo); // 5
'foo'변수는 Number 타입의 원시타입이다. addTwo() 함수와 addTwo_v2() 함수는 각각 같은 작업을 시도하는 다른 함수이다. 'foo' 변수의 값을 각각 함수에 인자로 전달한 결과는 둘 다 5이다. 두 번째 출력에서 7이 되지 않고 5가 출력되는 이유가 불변성 때문이다.
우선 두 함수는 인자 값 foo를 찾게 된다. 찾은 foo를 매개 변수로 전달하고 함수 내 로직을 실행하게 된다. 이때 로직을 실행하기에 앞서 Javscript는 전달된 매개변수의 값을 복사해 복사본을 생성한다. 그렇다면 addTwo() 함수와 addTwo_v2() 함수의 스코프내에서 복사본이 존재하는 것이다.
이 때 복사본은 함수 내의 식별자를 통해 접근하게 된다. addTwo() 함수는 num을 통해서, addTwo_v2() 함수는 foo를 통해서 접근하게 된다. addTwo() 함수에 로컬 인수 num이 생겨났고, 해당 값은 5가 되는 것이다. 이 값이 foo를 가리키는 것이 아니다. addTwo_v2() 함수도 마찬가지로 foo 로컬 인수가 생겨났고, 해당 값은 5이지 원래 foo를 가르키는 것이 아니다. 여기서 addTwo_v2() 함수 밖의 'foo'에는 접근할 수 없는데 이것은 렉시컬 스코핑과 변수 섀도잉 때문이다. 이것은 클로저와 관련이 있다.
복사본을 통해 값을 2 증가시켰기 때문에 둘 다 5가 출력되는 것이다. 따라서 원래의 'foo' 변수는 변화가 없는 것이고 이것이 불변성인 것이다.
다시 다른 예제를 통해 확인해보자.
let num = 23;
num = 27;
'num'이라는 변수에 23을 할당하고, 다음 줄에서 27로 재할당을 한다. 처음 23으로 할당을 했을 때는 메모리에 23이라는 값이 생성되는 것이고 num은 메모리의 23이라는 값을 가리킨다.
값을 27로 재할당한 두 번째 줄에서 메모리 상의 23이라는 값이 27로 바뀌는 것이 아니라 메모리에 새로운 공간을 할당해 27을 생성하고 num이 이 27이라는 값을 가리키는 것이다. 따라서 변수에 값을 재할당 하는 것이 기존 값을 변경했기 때문에 "불변성이 아니지 않나?"라고 생각할 수 있지만 원래 값은 메모리 공간에 남아 있고, 재할당한 값을 변수가 가르키는 것이다.
let num1 = 23;
let num2 = num1;
num1 = 27;
console.log(num1, num2); // 23, 27
num2 변수에 num1을 할당하게 되면 num2 메모리 공간에 num1의 값 23을 복사하게 된다. 그 후에 num1에 27을 재할당하더라도 num2는 영향을 받지 않는다.
참조타입은 원시타입을 제외한 나머지 모두
서두에 적었듯 Javscript 언어의 타입은 원시 값과 객체(Object)로 나뉜다고 했는데, 이 말이 곧 원시타입을 제외한 나머지 모두는 참조타입을 말한다. 문서를 조금 더 살펴보면 다음과 같이 설명이 되어 있다.
Javscript에서 원시 값(primitive, 또는 원시 자료형)이란
객체가 아니면서 메서드도 가지지 않는 데이터입니다.
참조타입은 원시타입이 아닌 모두이기 때문에 참조타입은 당연히 불변성을 갖고 있지 않다. 참조타입은 크기가 동적으로 변경될 수 있기 때문이다. 크기가 동적으로 변경 될 수 있기 때문에 원시타입과 달리 별도의 메모리 공간(Heap)에 저장되고 변수를 할당할 때 참조타입 데이터의 주소(메모리 상의 주소, Heap 메모리 주소 값)가 저장되고 이 주소를 활용해 변수 값에 접근하게 된다.
let a = 27;
let name = "MJ";
let arr = [];
'arr'라는 배열을 생성하면 메모리 공간에 Heap 메모리 주소 값이 저장되어 있고 추후에 'arr' 변수를 통해 접근을 하면 Heap 주소 값을 통해 접근하게 되는 것이다. 참조타입의 변수는 실제 데이터가 저장되어 있는 Heap 주소를 참조하기 때문에 변수를 수정하거나 할 때 유의해야 한다. 대표적인 예로 sort() 함수가 그런 케이스다. 배열을 정렬하기 위해 sort() 함수를 이용했고, 이 정렬된 함수를 이용한다고 했을 때, 원본 데이터가 수정되기 때문에 꼭 주의해야 한다. 배열은 참조타입이기 때문에.
정렬한 배열. 원 배열이 정렬되는 것에 유의하세요. 복사본이 만들어지는 것이 아닙니다.
참고
https://developer.mozilla.org/ko/docs/Glossary/Primitive
https://developer.mozilla.org/ko/docs/Web/JavaScript/Data_structures
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
'프로그래머스 > 데브코스 프론트엔드' 카테고리의 다른 글
[TIL] 2022-03-23/24 / 3, 4일차 (0) | 2022.03.24 |
---|---|
[자바스크립트] 이벤트 루프(Event Loop) (0) | 2022.03.23 |
[자바스크립트] 클로저(Closure) (0) | 2022.03.23 |
[TIL] 2022-03-22 / 2일차 (0) | 2022.03.22 |
[TIL] 2022-03-21 / 1일차 (0) | 2022.03.21 |