[모던 자바스크립트 Deep Dive]11장 원시 값과 객체의 비교
서론
원시 값과 객체의 차이는 크게 세 가지가 존재 한다.
- 원시 값은 변경 불가능 값, 객체는 변경 가능한 값이다.
- 변수에 원시 값을 할당하면 변수에는 실제 값이, 객체를 할당하면 참조 값이 저장된다.
- 변수: 확보된 메모리 공간
- 원시 값을 갖는 변수를 다른 변수에 할당시 값에 의한 전달이 일어나고, 객체를 가리키는 변수를 다른 변수에 할당할 때에는 참조에 의한 전달이 일어난다.
- 값에 의한 전달(pass by value): 원본의 원시 값이 복사되어 전달
- 참조에 의한 전달(pass by reference): 원본의 참조 값이 복사되어 전달
11.1 원시 값
11.1.1 변경 불가능한 값
- 한번 생성된 원시 값은 읽기 전용 값이다.
- 변수: 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 공간을 식별하기 위해 붙인 이름
- 값: 변수에 저장된 데이터, 표현식이 평가되어 생성된 결과
- 상수와 변경 불가능한 값은 같지 않다.
- 상수: 재할당이 금지된 변수 ```jsx // const 키워드를 사용해 선언한 변수는 재할당이 금지된다. // 상수는 재할당이 금지된 변수일 뿐이다. const o = {};
// const 키워드를 사용해 선언한 변수에 할당한 원시값(상수)은 변경할 수 없다. // 하지만 const 키워드를 사용해 선언한 변수에 할당한 객체는 변경할 수 있다. o.a = 1; console.log(o); // {a: 1} ```
- 원시 값은 변경 불가능하므로 데이터의 신뢰성을 보장한다.
- 원시 값은 변경 불가능하므로 원시 값을 할당한 변수에 새 원시 값을 재할당 시 새로운 메모리 공간을 확보한다.
11.1.2 문자열과 불변성
문자열
- 0개 이상의 문자로 이뤄진 집합
- 1개의 문자는 2바이트 메모리 공간에 저장
- 숫자는 값에 상관 없이 8바이트 공간에 저장되지만 문자열은 길이가 길어질 수록 필요한 바이트 수가 늘어난다.
- 유사 배열 객체이면서 이터러블
- 유사 배열 객체(array-like object): 배열처럼 인덱스로 프로퍼티 값에 접근할 수 있고
length
프로퍼티를 갖는 객체
- 유사 배열 객체(array-like object): 배열처럼 인덱스로 프로퍼티 값에 접근할 수 있고
불변성
1
2
3
4
5
6
7
var str = "string";
// 문자열은 유사 배열이므로 배열과 유사하게 인덱스를 사용해 각 문자에 접근할 수 있다.
// 하지만 문자열은 원시값이므로 변경할 수 없다. 이때 에러가 발생하지 않는다.
str[0] = "S";
console.log(str); // string
- 한번 생성된 문자열은 읽기 전용 값으로서 변경 불가
- 데이터의 신뢰성을 보장한다.
11.1.3 값에 의한 전달
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var score = 80;
// copy 변수에는 score 변수의 값 80이 복사되어 할당된다.
var copy = score;
console.log(score, copy); // 80 80
console.log(score === copy); // true
// score 변수와 copy 변수의 값은 다른 메모리 공간에 저장된 별개의 값이다.
// 따라서 score 변수의 값을 변경해도 copy 변수의 값에는 어떠한 영향도 주지 않는다.
score = 100;
console.log(score, copy); // 100 80
console.log(score === copy); // false
- 변수에 원시 값을 갖는 변수를 할당하면 할당받는 변수에는 할당하는 변수의 원시 값이 복사되어 전달
- 엄밀하게는 값이 아닌 식별자가 기억하고 있는 메모리 주소를 전달
- 할당 시점에 두 변수가 기존의 주소를 똑같이 기억하고 있는 방법과, 기존의 주소와 새로운 주소를 따로 기억하는 방법이 존재
- 엄밀하게는 값이 아닌 식별자가 기억하고 있는 메모리 주소를 전달
- 두 변수의 원시 값은 서로 다른 메모리 공간에 저장된 별개의 값
- 어느 한쪽의 값이 변경되더라도 다른 한쪽에는 영향이 없다.
11.2 객체
- 객체는 확보해야 할 메모리 공간의 크기를 사전에 정할 수 없다.
- 프로퍼티 값에는 제약이 없으며 동적으로 추가/삭제가 가능하다.
- 객체는 상대적으로 원시 값에 비해 많은 메모리를 소비한다.
따라서 자바스크립트 엔진은 해시 테이블과 유사하지만 더 나은 방법(히든 클래스)을 사용해 객체를 관리한다.
11.2.1 변경 가능한 값
- 객체(참조) 타입의 값, 즉 객체는 변경 가능한 값이다.
- 객체를 할당한 변수는 재할당 없이 객체를 직접 변경할 수 있다.
- 메모리에 저장된 객체를 직접 수정할 수 있다.
- 재할당이 없으므로 객체를 할당한 변수의 참조 값은 변경되지 않는다.
- 객체를 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면 참조 값에 접근할 수 있다.
- 참조 값: 객체가 저장된 메모리 공간의 주소
- 참조 값을 통해 실제 객체에 접근할 수 있다.
- “변수는 객체를 참고하고 있다” 또는 “변수는 객체를 가리키고(point) 있다”고 표현
- 원시 값의 경우는 “변수는 X값을 갖는다” 또는 “변수의 값은 X다”라고 표현
- 참조 값: 객체가 저장된 메모리 공간의 주소
객체 설계 특징
- 객체를 생성하고 관리하는 방식은 복잡하고 비용이 많이 소모
- 원시 값은 매번 이전 값을 복사해 새로 생성하면 명확하고 신뢰성이 확보
- 하지만 객체는 크기가 일정하지 않아 크기가 매우 커질 수 있고, 프로퍼티 값이 객체인 경우 복사하는 비용이 커 메모리를 효율적으로 쓰지 못하고 성능이 나빠지기에 이를 감안한 설계임
- 여러 개의 식별자가 하나의 객체를 공유할 수 있는 부작용 존재
얕은 복사와 깊은 복사
- 얕은 복사: 객체를 프로퍼티 값으로 갖는 객체에서 한 단계까지만 복사
1 2 3 4 5
const o = { x: { y: 1 } }; const c1 = { ...o }; // 35장 "스프레드 문법" 참고 console.log(c1 === o); // false console.log(c1.x === o.x); // true
- 복사로 생성된 객체는 원본과는 다른 객체
- 객체에 중첩된 객체의 참조 값을 복사
- 객체를 할당한 변수를 다른 변수에 할당하는 것을 지칭하기도 함
1 2 3 4 5
const o = { x: 1 }; // "얕은 복사"라고 부르기도 한다. const c2 = o; console.log(c2 === o); // true
- 깊은 복사: 객체를 프로퍼티 값으로 갖는 객체에서 객체에 중첩된 객체를 모두 복사
1 2 3 4 5 6 7 8
const o = { x: { y: 1 } }; // 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
- 복사로 생성된 객체는 원본과는 다른 객체
- 객체에 중첩된 객체까지 모두 복사
- 원시 값을 할당한 변수를 다른 변수에 할당하는 것을 지칭하기도 함
1 2 3 4 5
const v = 1; // "깊은 복사"라고 부르기도 한다. const c1 = v; console.log(c1 === v); // true
11.2.2 참조에 의한 전달
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var person = {
name: "Lee",
};
// 참조값을 복사(얕은 복사). copy와 person은 동일한 참조값을 갖는다.
var copy = person;
// copy와 person은 동일한 객체를 참조한다.
console.log(copy === person); // true
// copy를 통해 객체를 변경한다.
copy.name = "Kim";
// person을 통해 객체를 변경한다.
person.address = "Seoul";
// copy와 person은 동일한 객체를 가리킨다.
// 따라서 어느 한쪽에서 객체를 변경하면 서로 영향을 주고 받는다.
console.log(person); // {name: "Kim", address: "Seoul"}
console.log(copy); // {name: "Kim", address: "Seoul"}
- 객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조 값이 복사되어 전달
- 원본 변수와 사본 변수는 각기 다른 메모리 주소에 동일한 참조 값을 가지고 있다.
- 두 개의 식별자가 하나의 객체를 공유하게 되므로 어느 한쪽에서 객체를 변경하면 둘 다 영향을 받는다.
This post is licensed under CC BY 4.0 by the author.