본문 바로가기

JS

JS 원시 데이터 타입과 객체 비교

1. 원시 데이터 타입과 객체 비교 

 

자바스크립트에서는 원시 데이터 타입으로 문자형, 숫자형, 논리형, null, undefined, symbol 타입을 제공하고 있다.

그리고 이외의 값에 대해서는 모두 객체로 다루고 있다.

 

이 두 가지 타입에 대해서는 다음과 같은 측면에서 구분될 수 있다.

 

1️⃣ 변경 가능 여부

 

원시 데이터 타입은 변경이 불가능한 값(immutable value) 이고, 객체는 변경이 가능한 값(mutable value) 이다.

 

원시 데이터 타입으로 생성된 값은 읽기 전용 값으로, 변경이 불가능한 타입이다.

 

이에 대해서는 변수와 값을 구분해서 생각해야 한다.

변수는 하나의 값을 저장하기 위해 확보된 메모리 공간을 식별하기 위하여 붙여진 식별자이고, 

값은 메모리 공간에 저장되는 실제 데이터로 표현식이 평가되어 생성된 결과를 말한다.

 

여기에서 변경이 불가능한 부분은 변수가 아닌 바로 값의 영역이다.

즉, 변수에 대해서는 재할당을 통해 언제든지 교체가 가능하지만, 값에 대해서는 불가능하다는 것이다.

var a = 1;

자바스크립트에서는 위의 코드를 실행하기 전, 변수에 값을 할당하기 위하여 자바스크립트 엔진을 통하여 선언부를 먼저 호이스팅한다.

이 때, 호이스팅 된 변수 a 에는 1 이 아닌 undefined 값이 초기화 값으로 선언된다.

이후 해당 코드가 런타임을 거치며 undefined 에서 1 로 재할당이 일어나는 것이다.

var a;        // undefined

var a = 1;    // 1

 

이렇게만 봐서는 마치 값이 변경된 것 같은 느낌이지만, 앞서 값은 변경이 불가능한 값이라고 하였다.

엄밀히 말하면 위의 코드에서는 값이 변경된 것이 아니라 변수가 가리키는 주소의 값이 달라진 것이다.

var a;       // 0x00000001 👉 undefined

var a = 1;   // 0x00000002 👉 1

위에서 변수는 메모리 공간을 식별하기 위하여 붙여진 식별자라고 하였다.

이 식별자는 메모리 공간에 대한 주소값을 가리키게 되고 이를 통하여 해당 공간에 저장된 값을 찾게 된다.

 

즉, 변수 선언 시에 undefined 값을 저장하기 위하여 공간이 마련되었고, 이 공간의 주소값을 변수 a 가 기억하게 된다.

이후 재할당을 통하여 변수 a 는 기존에 기억하고 있던 주소값을 지우고, 새로운 주소값을 기억하며 전혀 다른 장소를 가리키게 된다.

이 때 여전히 undefined 값이 저장된 공간이 존재하고 있으며, 이를 참조하는 변수가 일정 시간 동안 나타나지 않으면 GC 에 의하여 해당 데이터는 사라지게 된다.

 

따라서 원시 데이터 타입은 메모리 공간에 생성된 결과를 한 번 저장하게 되면 변경하지 못하지만, 변수가 다른 주소값을 기억하도록 하여 값의 재할당을 구현하는 것이다. 

 

이와 달리 객체는 변경이 가능한 값이다.

객체는 값을 할당하게 되면 할당된 값을 저장하고 있는 실제 주소값을 참조하게 된다.

var person = {
	name : 'Joon'
};

위와 같은 객체가 있다고 가정하였을 때에 대해 이야기 해보자.

선언문은 호이스팅에 의하여 먼저 undefined 값으로 초기화가 되고, 런타임 시에 해당 코드가 실행되면서 객체 리터럴로 해석하며 객체 리터럴이 메모리에 할당된다. 

이 때 객체 리터럴이 할당된 메모리의 주소값을 바로 변수 person 이 기억하게 되는 것이다. 

이러한 부분에 대하여 변수 person 은 객체를 참조하고 있다고 하는 것이다. 

 person 👉 0x00000001 👉 0x00000002
                   0x00000002 👉 {name : 'Joon'}

객체는 변경이 가능한 값이기 때문에 메모리에 저장된 객체를 직접 수정 또는 삭제가 가능하다.

var person = {
	name : 'Joon'
}

// 프로퍼티 수정
person.name = 'Sun';

// 프로퍼티 추가
person.age = 24;

// 프로퍼티 삭제
delete person.age;
person 👉 0x00000001 👉 0x00000002
                   0x00000002 👉 {name : 'Sun'}

메모리 주소를 확인해 보면, 객체 내부의 데이터가 수정되었을 뿐 변수 person 이 참조하고 있는 주소값은 변하지 않았다는 것을 확인할 수 있다. 

이는 객체를 할당한 변수 person 에 다른 객체 주소값을 재할당을 하지 않았기 때문이다. 

 

만약 객체 또한 변경 불가능한 타입이었다면 값의 변동이 있을 때마다 새로운 메모리 공간을 확보해야 한다는 부담이 생긴다. 

객체는 자유롭게 수정 또는 삭제가 가능한 만큼 크기가 어느 정도일지 알 수 없기에 그 부담은 더욱 커질 수 있고 이는 메모리 활용의 효율에도 악영향을 미칠 수 있다

이러한 이유로 객체는 변경 가능한 타입이 된 것이다.  

 

📌 객체의 구조적 단점

객체의 이러한 구조적 특징으로 인한 단점도 존재한다.

바로 원시 타입과 달리 여러 개의 식별자가 같은 객체를 참조할 수 있다는 것이다.

var person = {
	name : 'Joon',
    age : '26'
}

var copy = person;

예를 들어 위와 같은 코드를 작성했다고 해보자.

이 때 변수 copy 는 person 이 가리키고 있는 객체의 주소값을 복사하여 저장하게 된다.

즉 두 개의 다른 변수가 같은 객체의 주소값을 참조하게 된다. (이러한 경우를 얕은 복사라고 하는데, 이는 후술하기로 한다.)

 

이 때의 문제점은 바로 같은 객체를 참조하고 있다는 점이다.

만약 변수 copy 를 통하여 객체에 접근해 무언가 변경하게 된다면 변경된 사항은 변수 copy 에만 반영되는 것이 아니라

해당 객체를 참조하고 있는 변수 person 에도 영향을 끼치게 된다.

var person = {
	name : 'Joon',
    age : 26
}

var copy = person;

// 변수 person 의 프로퍼티 name 변경
person.name = 'Sun';

// 변수 copy 의 프로퍼티 age 변경
copy.age = 15;

위와 같이 변경을 하게 되었을 때 기대하게 되는 결과는 다음과 같을 것이다. 

var person = {
	name : 'Sun',
    age : 26
};

var copy = {
	name : 'Joon',
    age : 15
};

하지만 예상과는 달리 다음과 같은 결과를 얻게 된다.

var person = {
	name : 'Sun',
    age : 15
};

var copy = {
	name : 'Sun',
    age : 15
};

이러한 결과를 얻게 된 이유는 바로 변수 copy 는 변수 person 이 저장하고 있는 값을 얕은 복사하였기 때문에

참조하고 있는 객체의 주소값만을 복사하였기 때문이다. 

 

객체는 해당하는 주소값만 알고 있으면 얼마든지 접근 가능하기 때문에 결과적으로 copy 와 person 은 서로에게 영향을 주게 된 것이다.

 

따라서 객체를 복제하여 별도로 사용하고 싶다면 얕은 복사가 아닌 깊은 복사를 사용해야 한다.

 

 

2️⃣ 변수에 할당된 값

 

원시 데이터 타입에 값을 할당하게 되면 해당 메모리 공간에는 해당 값이 저장되지만,
객체를 변수에 할당하게 되면 해당 메모리 공간에는 객체를 참조하기 위한 주소값이 저장된다.

 

예를 들어 다음과 같은 코드를 작성했다고 해보자.

var a = 1;

var b = {
	num : 2
};

변수 a 에는 1 이 숫자형으로 해석되어 값을 할당하게 될 것이고, 

변수 b 에는 {num : 2} 이 객체로 해석되어 값을 할당하게 될 것이다. 

 

이 때의 차이점은 어떤 데이터 타입으로 해석되는지가 중요하다.

변수 a 처럼 원시 데이터 타입으로 해석되면 변수 a 의 메모리 공간에는 1 이 저장되게 된다.

하지만 변수 b 처럼 객체 타입으로 해석되는 경우에는 해당 메모리 공간에 객체가 저장된 실제 메모리 공간의 주소값을 저장하게 된다.

변수 b 는 이 주소값을 통하여 객체에 접근할 수 있게 되는데 이를 객체를 참조하고 있다라고 해석한다.

a 👉 1
b 👉 0x00000001

 

3️⃣ 다른 변수에 할당

 

원시 데이터 타입을 가진 변수를 다른 변수에 할당하게 되면 원시 값이 복사되어 전달되고, 
객체 타입을 가진 변수를 다른 변수에 할당하게 되면 참조하고 있는 주소값이 전달된다.

 

위의 차이점과 이어지는 이야기로, 결국은 각 변수가 저장하고 있는 데이터 값이 복사된다.

원시 데이터 타입의 경우를 값에 의한 전달(pass by value)이라고 하고, 객체 타입의 경우를 참조에 의한 전달(pass by reference) 라고 구분하고 있지만, 결과적으로는 모두 값에 의한 전달이 되는 것이다. 

 

앞서 원시 데이터 타입으로 해석된 경우에는 변수에 해석된 결과가 그대로 담기게 되었지만, 

객체 타입으로 해석된 경우에는 변수에 해당 객체가 담긴 주소값이 담기게 된다고 하였다.

 

이로 인하여 다른 변수에 할당할 때에 각각 담긴 내용을 할당하게 될 때 차이가 생기는 것이다. 

var a = 1;

var b = {
	num : 2
};

var c = a;

var d = b;

이러한 경우 변수 c 에는 변수 a 에 저장된 숫자형 1 값이 할당되는 것이고,

변수 d 에는 변수 b 에 저장된 객체 주소값 0x00000001 이 할당되는 것이다.

 

각 데이터 타입에 따라 저장하고 있는 내용에 차이가 있을 뿐, 값을 전달한다는 점에서는 같다.

 

'JS' 카테고리의 다른 글

JS 객체 : Object  (0) 2022.11.14
JS 데이터 타입 변환 : 암묵적, 명시적  (0) 2022.11.12
JS 제어문 : 조건문, 반복문  (0) 2022.11.12
JS 연산자 : 산술, 비교, 논리, 대입  (1) 2022.11.11
JS 데이터 타입  (0) 2022.11.03