Post

Deep Dive 11. 원시 값과 객체의 비교

원시 값

변경 불가능한 값

원시 타입의 값은 변경 불는한 값(immutable value)이다. 한번 생선된 원시 값은 읽기 전용값으로서 변경할 수 없다. 원시 값을 변수에 할당하면 변수(확보된 메모리 공간)에는 실제 값이 저장된다.

Desktop View 원시 값은 변경이 불가능하기 때문에 변수에 재할당 될 때마다 메모리 공간을 새로 확보하고 원시 값을 저장해야 한다. 이러한 값의 특성을 불변성(immutablility)라고 한다. 주의해야 할 점은 변경 불가능하다는 것은 변수가 아니라 값에 대한 진술로 변수에는 새로운 원시 값이 저장된 메모리 공간의 주소를 할당함으로써 값을 변경할 수 있다.

문자열과 불변성

C 에서는 문자열을 문자의 배열로 처리하고 자바에서는 문자열을 String 객체로 처리한다. 하지만 자바스크립트는 개발자의 편의를 위해 원시 타입인 문자열 타입을 제공한다.

문자열은 유사 배열 객체이면서 이터러블이므로 배열과 유사하게 각 문자에 접근할 수 있다. 유사 배열 객체란 마치 배열처럼 인덱스로 프로퍼티 값에 접근할 수 있고 length 프로퍼티를 갖는 객체를 지징한다.

1
2
3
4
5
6
7
8
var str = 'string';

// 문자열은 유사 배열이므로 배열과 유사하게 인덱스를 사용해 각 문자에 접근할 수 있다.
console.log(str[0]); // s

// 원시 값인 문자열이 객체처럼 동작한다.
console.log(str.length); // 6
console.log(str.toUpperCase()); // STRING

주의해야 할 점은 문자열은 결국 원시 값으로 어떤 일이 있어도 불변한다는 것이다.

1
2
3
4
5
6
7
var str = 'string';

// 문자열은 유사 배열이므로 배열과 유사하게 인덱스를 사용해 각 문자에 접근할 수 있다.
// 하지만 문자열은 원시 값이므로 변경할 수 없다. 하지만 에러는 발생하지 않는다.
str[0] = 'S';

console.log(str); // string

객체

객체는 프로퍼티의 개수가 정해져 있지 않으며, 동적으로 추가되고 삭제할 수 있다.

자바스크립트 객체는 프로퍼티 키를 인덱스로 사용하는 해시 테이블(hash table)이라고 생각할 수 있지만 높은 성능을 위해 일반적인 해시테이블보다 나은 방법으로 객체를 구현한다. 자바스크립트는 클래스 없이 객체를 생성할 수 있으며 객체가 생성된 이후라도 동적으로 프로퍼티와 메서드를 추가할 수 있다. 편리하기는 하지만 이론적으로 클래스 기반 객체지향 프로그래밍 언어의 객체보다 생성과 프로퍼티 접근에 비용이 더 많이 드는 비효율적인 방식이다. 따라서 V8 자바스크리브 엔진에서는 프로퍼티에 접근하기 위해 동적탐색 대신 히든 클래스라는 방식을 사용해 C++객체의 프로퍼티에 접근하는 정도의 성능을 보장한다.

변경 가능한 값

객체(참조) 타입의 값, 즉 객체는 변경 가능한 값(mutable value)이다.

Desktop View

원시 값을 할당한 변수를 참조하면 메모리에 저장되어 있는 원시 값에 접근한다. 하지만 객체를 할당하는 변수를 참조하면 메모리에 저장되어 있는 참조 값을 통해 실제 객체에 접근한다.

객체를 할당한 변수는 재할당 없이 객체를 직접 변경할 수 있다.

1
2
3
4
5
6
7
// 프로퍼티 값 갱신
person.name = 'Kim';

// 프로퍼티 동적 생성
person.address = 'Seoul';

console.log(person); // { name: "Kim", address: "Seoul" }

객체를 변경 할 때마다 원시 값처럼 이전 값을 복사해서 새롭게 생성한다면 명확하고 신뢰성이 확보되겠지만 객체는 크기가 매우 클 수도 있고, 원시 값처럼 크기가 일정하지도 않으며, 프로퍼티 값이 객체일 수도 있어서 복사(deep copy)해서 생성하는 비용이 많이든다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const o = { x : { y: 1}};

// 얕은 복사
const c1 = { ...o };
console.log(c1 === o); // false
console.log(c1.x === o.x) // true

// lodash의 cloneDeep을 사용한 깊은 복사
// "npm install lodash"로 lodash를 설치한 후, Node.js 환경에서 실행
const _ = require('lodash');
// 깊은 복사
const c2 = _.cloneDeep(o);
console.log(c2 === o); // false
console.log(c2.x === o.x) //false

위 코드의 o에는 value가 객체인 프로퍼티를 가지고 있다. 스프레드 문법을 사용하여 oc1에 복사하는 경우 얕은 복사로 oc1에는 다른 참조 값이 할당되지만 중첩되어 있는 객체의 참조 값은 동일하다. 동일한 참조 값을 다른 변수에 담는 것은 예기치 못한 에러를 불러올 수 있다. 따라서 lodash를 설치하여 깊은 복사를 해야 한다.

This post is licensed under CC BY 4.0 by the author.