[js] 원시 값과 객체의 비교

김효식 (HS KIM)·2021년 11월 28일
1

자바스크립트는 총 8가지 데이터 타입을 아래와 같이 제공한다.
Number, String, Boolean, undefined, null, Symbol, BigInt, Object
이 중에서 객체 타입Object를 제외한 데이터 타입은 모두 원시 타입으로 구분한다. 원시 타입객체 타입의 차이점을 명확히 구분하지 못한다면, 기대하는 값과 실제 값이 다른 상황을 자주 겪게 될 것이다.

원시 타입과 객체 타입으로 구분하는 이유

객체를 제외한 다른 데이터 타입들은 모두 원시 타입이라고 했는데, 별다른 이유없이 객체만 다른 데이터 타입들과 다르게 구분될리가 없다. 객체는 다른 데이터 타입들과 크게 3가지 측면에서 다르다.

immutable vs mutable

원시(타입의) 값변경 불가능한(immutable) 값이지만, 객체(타입의) 값변경 가능한(mutable) 값이다.

여태까지 변수의 값을 한두번 변경한게 아닌데 변경 불가능한 값이라니 당황스럽기 그지 없다.
그러나, 사실 우리는 단 한번도 변수의 원시값을 직접 변경한 적은 없다.

변수의 값 !== 원시값

변수의 값은 원시값과 같지 않다. 변수에는 원시값이 담겨 있는 메모리 공간의 주소가 값으로 담겨 있다. 여태까지 변수의 값을 변경했다면, 그것은 원시값이 담겨 있는 메모리 공간의 주소를 변경한 것이다.

var a = 1;
a = 2;

변수 a에 1이라는 값을 할당하면, 런타임 이전에 undefined로 값을 초기화 하고 새로 확보한 메모리 공간에 재할당한 값 1을 넣는다. 이 시점에 왼쪽 그림을 보면 변수 a에는 값 1이 담겨있는 메모리 공간이 담겨져 있다. 그런데 변수 a에 값을 2로 재할당하면 메모리 공간에 담겨져 있는 1이라는 값이 변경되는 것이 아니라, 새로운 메모리 공간을 확보하고 2라는 값을 넣었다. 그리고 변수 a는 2라는 값이 있는 새롭게 확보된 메모리 공간을 가리키고 있다.

그러므로 원시값을 변경할 수 없다는 것은 변수의 값을 변경하지 못한다는 것이 아니라, 원시값 자체를 변경할 수 없다는 의미이고 좀 더 구체적으로는원시값을 가진 메모리 공간의 값에 대한 진술을 변경할 수 없다는 의미다.

실제 값 vs 참조 값

원시(타입의) 값을 변수에 할당하면 실제 값이 저장되지만, 객체(타입의) 값참조 값이 저장된다.

var primitive = true;
var obj = { a : true };

위에서 변수에 원시 타입의 값을 할당했을 때, 메모리 공간에서 어떻게 동작하는지 확인했다.
primivite 변수에도 원시 타입의 값을 할당했으므로, 메모리 공간에는 실제 값(true)이 담기게 된다.
그런데 객체 타입은 다르게 동작한다. 객체 타입은 실제 값이 담겨져 있는 메모리 공간의 주소를 별도의 메모리 공간에 확보하고, 실제 값이 담긴 메모리 주소를 값으로 가지고 있다. 그러므로 변수에 담겨져 있는 메모리 주소는 객체의 실제 값이 아닌 실제 값이 담겨져 있는 메모리 주소를 값으로 가지고 있다.

원시 값 vs 참조 값

원시(타입의) 값을 가진 변수를 다른 변수에 할당하면, 원시 값이 복사되어 전달되지만, 객체(타입의) 값참조 값이 복사되어 전달된다.
이를 각각 값에 의한 전달(pass by value)참조에 의한 전달(pass by reference)이라고 한다.

var primitive = true;
var obj = { a : true };

var copyPrimitive = primitive;
var copyObj = obj;

원시타입의 값과 객체 타입의 값을 각각 다른 변수에 재할당 했을 때 값이 복사하는 값이 다르다. 원시 타입은 원시 값을 복사하고, 객체 타입은 참조 값을 복사한다. 복사하는 값의 유형은 달라보이지만, 원시타입의 값실제 값이 담겨져 있고 객체 타입의 값참조 값이 변수의 값이므로 변수에 담겨져 있는 값을 그대로 복사한다는 점에서는 다르지 않다.

그런데 원시타입실제 값을 복사하기 때문에 상관 없지만, 객체타입참조 값을 복사하기 때문에 하나의 객체를 공유하는 문제가 발생한다.

console.log(copyPrimitive) // true
copyPrimitive = false;
console.log(copyPrimitive) // false
console.log(primitive) // true

console.log(copyObj.a) // true
copyObj.a = false;
console.log(copyObj.a) // false
console.log(obj.a) // false

원시타입은 다른 변수에 할당한 변수의 값을 변경하면 기존 변수의 값에 어떠한 영향도 끼치지 않는다. 새로 할당한 변수에는 기존 변수의 값을 그대로 복사한 값만 담겨 있을 뿐, 기존 변수와는 전혀 상관이 없다.
그런데 객체는 다른 변수에 할당한 변수의 값을 변경하면 기존 변수의 값에도 영향을 끼친다. 객체는 실제 값이 아닌 참조 값을 복사해서, 새로 할당한 변수에도 기존 변수가 참조하고 있던 메모리 공간의 주소를 값으로 가지고 있기 때문이다. 그렇기 때문에 objcopyObj는 같은 객체를 공유하고 있어서, 하나의 변수에서 객체의 값을 수정하면 다른 객체에도 영향을 끼치게 된다.

그렇다면 객체는 왜 다른 원시값들과 다르게 동작할까? 이유는 객체는 다른 원시 타입들에 비해 메모리 공간을 훨씬 많이 차지하기 때문이다. 시간이 갈수록 메모리의 제약은 덜해지고 있지만, 자바스크립트라는 언어가 세상에 나왔을 당시에는 메모리의 제약이 상당히 심했다. 그렇기 때문에 메모리를 많이 차지하는 객체를 새로운 변수에 할당할 때마다, 새로운 값을 메모리 공간에 할당하기에는 메모리의 크기를 고려했을 때 무리가 있었다. 그래서 새로운 변수에 값을 복사하더라도 메모리 공간을 추가적으로 확보할 필요가 없는 방식으로 고안된 것이다.
결과적으로 객체의 특성 때문에 객체는 다른 원시값들과 다른 점들 때문에 고려해야 될 부분이 많아졌다.

profile
자기개발 :)

0개의 댓글