Post

[모던 자바스크립트 Deep Dive]11장 원시 값과 객체의 비교

서론

원시 값과 객체의 차이는 크게 세 가지가 존재 한다.

  1. 원시 값은 변경 불가능 값, 객체는 변경 가능한 값이다.
  2. 변수에 원시 값을 할당하면 변수에는 실제 값이, 객체를 할당하면 참조 값이 저장된다.
    • 변수: 확보된 메모리 공간
  3. 원시 값을 갖는 변수를 다른 변수에 할당시 값에 의한 전달이 일어나고, 객체를 가리키는 변수를 다른 변수에 할당할 때에는 참조에 의한 전달이 일어난다.
    • 값에 의한 전달(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 프로퍼티를 갖는 객체

불변성

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. 여러 개의 식별자가 하나의 객체를 공유할 수 있는 부작용 존재

얕은 복사와 깊은 복사

  • 얕은 복사: 객체를 프로퍼티 값으로 갖는 객체에서 한 단계까지만 복사
    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.