자바스크립트에서의 데이터 타입은 크게 원시 타입과 객체 타입으로 구분할 수 있다.
- 원시 값은 변경 불가능한 값이다. 하지만 객체는 변경 가능한 값이다.
- 원시 값을 변수에 할당하면 변수에는 실제 값이 저장된다. 객체를 변수에 할당하면 변수에는 참조 값이 저장된다.
- 원시 값을 갖는 변수를 다른 변수에 할당하면 원본의 원시 값이 복사되어 전달된다. 이에 비해 객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조 값이 복사되어 전달된다.
원시 값
변경 불가능한 값
원시 타입의 값은 변경 불가능한 값이다. 한번 생성된 원시 값은 읽기 전용으로 변경할 수 없다. 변경 불가능하다는 것은 데이터의 신뢰성을 보장한다. 여기서 변경 불가능 하다는 것은 메모리 공간에 저장된 값이 변경 불가하다는 의미이다. 변수는 할당을 통해 값을 변경할 수 있다. 따라서 재할당이 이뤄지면 기존에 저장된 값이 변경되는 것이 아닌 값이 새로운 메모리 공간에 저장되고 변수가 참조하던 메모리 공간이 바뀐다.
문자열과 불변성
원시 타입별로 메모리 공간의 크기가 정해져 있다. 문자열은 다른 원시 값과 비교할 때 특징이 있다. 문자열은 0개 이상의 문자로 이뤄진 집합을 말하며, 1개의 문자는 2바이트의 메모리 공간에 저장된다. 문자열은 몇 개의 문자로 이뤄졌느냐에 따라 필요한 메모리 공간의 크기가 결정된다. 문자열은 유사 배열 객체이면서 이터러블이므로 배열과 유사하게 각 문자에 접근할 수 있다.(원시 값과 래퍼 객체) 하지만 이미 생성된 문자열의 일부를 변경해도 원시값이기 때문에 반영되지 않는다.
값에 의한 전달
변수에 원시 값을 갖는 변수를 할당하면 할당 받는 변수에는 할당되는 변수의 값이 복사되어 전달된다. 이를 값에 의한 전달이라 한다. 즉 할당 받는 변수에 할당되는 값은 다른 메모리 공간에 저장된 새로운 값이다. 조금더 엄밀히 살펴보면 자바스크립트에서 변수가 동작하는 방식에 따라 변수에는 값이 아니라 값이 저장된 메모리 주소가 저장(전달)된다. 여기서 두 가지 방법이 가능한데, 값의 메모리 주소를 전달하고 기존 변수의 값이 변경되었을때 값을 생성해 메모리 주소를 교체하는 것과, 값을 새롭게 생성하고 생성된 값의 메모리 주소를 전달하는 것이다. 자바스크립트 사양에서 이를 정확히 명시하지 않기 때문에 자바스크립트 엔진에 따라 다르게 구현될 수 있다. 하지만 공통적으로 변수에 원시 값을 갖는 변수를 할당하면 메모리 주소가 전달되고, 전달된 메모리 주소를 통해 메모리 공간에 접근하면 값을 참조할 수 있다. 또한 결론적으로 두 변수의 원시 값은 서로 다른 메모리 공간에 저장된 별개의 값이 되기 때문에 한쪽에서 재할당을 통해 값을 변경하더라도 서로 간섭할 수 없다.
객체
객체는 프로퍼티의 개수와 값의 타입에 제약이 없다. 따라서 객체는 원시 값과 같이 확보해야 할 메모리 공간의 크기를 사전에 정해 둘 수 없다. 이러한 객체를 관리하기 위해서 객체는 원시 값과 다른 방식으로 동작한다. 객체는 해시 테이블과 유사한 형태로 구현되어있다.
변경 가능한 값
객체는 변경 가능한 값이다. 변수에 객체를 할당하면 변수는 객체의 참조 값을 저장한다. 참조 값은 생성된 객체가 저장된 메모리 공간의 주소이다. 변수는 이 참조 값을 통해 객체에 접근할 수 있다. 원시 값은 변경 불가능한 값이므로 원시 값을 갖는 변수의 값을 변경하려면 재할당 외에는 방법이 없다. 하지만 객체는 변경 가능한 값이다. 따라서 객체를 할당한 변수는 재할당 없이 객체를 직접 변경할 수 있다. 즉, 재할당 없이 프로퍼티를 동적으로 추가/갱신/삭제가 가능하다. 이때 객체를 할당한 변수에 재할당을 하지 않았으므로 객체를 할당한 변수의 참조 값은 변경되지 않는다. 객체가 이렇게 동작하는 이유는 객체를 원시 값처럼 변경 불가능하게 사용하려면 객체를 변경할 때마다 이전 객체를 복사해서 값을 새롭게 생성해야 하는데 이는 비용이 많이 드는 일이다. 따라서 데이터의 신뢰성이 떨어지는 것을 감안하고 객체의 변경을 허용하고, 변수에는 객체의 참조값을 저장하도록 설계한 것이다. 이 때문에 발생한 부작용이 있는데, 여러 개의 식별자가 하나의 객체를 공유할 수 있다는 것이다.
참조에 의한 전달
객체를 가리키는 변수를 다른 변수에 할당하면 객체를 가리키는 참조 값이 복사되어 전달된다. 이를 참조에 의한 전달이라 한다. 따라서 두 변수(원본, 사본)의 값은 저장된 메모리 공간은 다르지만 동일한 객체의 참조 값으로 같다. 즉, 두 변수는 같은 객체를 가리킨다.(참조한다) 이 때문에 두 개의 변수가 하나의 객체를 공유하게 된다. 따라서 원본과 사본 중 어느 한쪽에서 객체를 변경(프로퍼티 추가/삭제/변경)하면 서로 영향을 받는다.
위와 같이 동작 원리를 살펴보면 값에 의한 전달과 참조에 의한 전달은 식별자가 기억하는 메모리 공간에 저장되어 있는 값을 복사해서 전달한다는 면에서 동일하다. 따라서 자바스크립트에는 참조에 의한 전달은 존재하지 않고 값에 의한 전달만이 존재한다고 말할 수 있다.
얕은 복사와 깊은 복사
변수에 다른 변수에 저장된 객체를 저장할 때 원본과 사본이 서로 달라야(독립적)하는 경우가 있다. 이를 위해서 할당할 변수에는 기존 객체의 복사본을 저장하게 되는데 이때 주의할 점이 있다.
얕은 복사는 객체를 스프레드 문법 등으로 복사했을때의 동작 방식이다. 얕은 복사로 생성한 객체는 원본과는 다른, 참조 값이 다른 별개의 객체다. 하지만 객체 내부에(프로퍼티 값) 중첩된 객체가 있는 경우, 프로퍼티는 원본과 사본이 동일한 참조 값을 갖게 된다. (참조 값을 복사하기 때문에) 따라서 이를 방지하고 서로 독립적인 다른 객체로 생성하기 위해서는 깊은 복사를 해야 한다. 깊은 복사는 중첩 객체까지 모두 복사해서 원시 값처럼 완전한 복사본을 만든다는 차이가 있다. 깊은 복사를 위해서는 객체를 재귀적으로 복사하거나, lodash의 cloneDeep과 같은 라이브러리 메서드를 사용할 수 있다.
slice 메서드, 스프레드 문법, Object.assign 메서드는 모두 얕은 복사를 수행한다.
'개발 > 자바스크립트' 카테고리의 다른 글
13. 스코프 (0) | 2022.07.10 |
---|---|
12. 함수 (0) | 2022.07.05 |
10. 객체 리터럴 (0) | 2022.07.04 |
09. 타입 변환과 단축 평가 (0) | 2022.07.01 |
08. 제어문 (0) | 2022.07.01 |
댓글