Published on

원시 값과 객체의 비교

원시 값과 객체의 비교

원시 타입

  • 변경 불가능한 값(Immutable value)
  • 변수에 원시 값을 할당하면 실제 값이 저장
    • 원시 값이 복사되어 전달
      • 값에 의한 전달(pass by value)

객체 타입

  • 변경 가능한 값(mutable value)
  • 변수에 객체를 할당하면 변수에는 참조 값이 저장
    • 참조 값이 복사되어 전달
      • 참조에 의한 전달(pass by reference)

원시 값

  • 한번 생성된 원시 값은 읽기 전용값으로서 변경할 수 없음
    • 데이터의 신뢰성 보장
  • 변수
    • 하나의 값을 저장하기 위해 확보한 메모리 공간 자체를 식별
    • 변수에 저장된 데이터 → 표현식이 평가되어 생성된 결과

변경 불가능한 것은 에 대한 진술임

값의 불변성

js_1
  • 새로운 메모리 공간을 확보 후 재할당한 원시 값을 저장한 후, 변수는 새롭게 재할당한 원시 값을 가리킴
    • 참조하던 메모리 공간의 주소가 바뀐 이유는 원시 값이 변경 불가능한 값이기 때문임

불변성을 갖는 원시 값을 할당한 변수는 재할당 이외에 변수 값을 변경할 수 없음

문자열과 불변성

  • ECMAScript 사양에 문자열 타입(2바이트)과 숫자 타입(8바이트) 이외의 원시 타입 크기를 명확히 규정하고 있지 않음
    • 브라우저 제조사 마다 다름
  • 몇 개의 문자로 이루어졌느냐에 따라 필요한 메모리 공간의 크기가 결정됨
// 문자열은 0개 이상의 문자로 이뤄진 집합
var str = '';       // 0개의 문자로 이뤄진 문자열(빈 문자열)
var str2 = 'Hello'; // 5개의 문자로 이뤄진 문자열
  • 개발자의 편의를 위해 원시 타입인 문자열 타입 제공
  • 문자열은 유사 배열 객체이면서 이터러블이므로 배열과 유사하게 각 문자에 접근이 가능

유사 배열 객체(array-like object)

  • 배열처럼 인덱스로 프로퍼티 값에 접근이 가능하며 length 프로퍼티를 갖는 객체

값에 의한 전달

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
  • score 변수와 copy 변수의 값 80은 메모리 공간에 저장된 별개의 값
  • 원시 값을 갖는 변수를 할당하면 할당받는 변수(copy)에는 할당되는 변수(score)의 원시 값이 복사되어 전달됨
js_2
  • 변수에는 값이 전달되는 것이 아니라 메모리 주소가 전달됨
    • 변수와 같은 식별자는 값이 아니라 메모리 주소를 기억하고 있음
    • 전달된 메모리 주소를 통해 메모리 공간에 접근하여 값을 참조

주의 ⚠️

  • 실제 자바스크립트 엔진의 내부 동작과 정확히 일치하지 않을 수 있음
  • ECMAScript 사용에 변수를 통해 메모리를 어떻게 관리해야하는지 명확하게 정의되어 있지 않음
  • 값에 의한 전달이라는 용어도 사양에 정의되어 있지 않음(통상 타 언어에서 사용하는 용어)

객체

  • 프로퍼티의 개수가 정해져 있지 않음 → 동적으로 추가, 삭제 가능
  • 프로퍼티의 값에도 제약이 없음
  • 원시 값처럼 확보해야할 메모리 공간의 크기를 사전에 정해둘 수 없음
  • 원시 값에 비해 상대적으로 많은 크기를 소비할 수 있음

자바스크립트 객체의 관리 방식

  • 프로퍼티 키를 인덱스로 사용하는 해시 테이블과 유사하지만 높은 성능을 위해 일반적인 해시 테이블보다 나은 방법으로 객체를 구현함
js_3
  • 자바스크립트는 자바, C++ 같은 언어와 다르게 클래스 없이 객체를 생성할 수 있음
    • 객체 생성이후에 동적으로 프로퍼티와 메서드를 추가할 수 있음
    • 성능 면에서는 이론적으로 생성과 프로퍼티 접근에 비용이 더 많이 듦

V8 자바스크립트 엔진에서는 프로퍼티에 접근하는 방식(동적 탐색)을 히든 클래스 방식을 사용해 C++ 객체의 프로퍼티에 접근하는 정도의 성능을 보장함

  • 자바와 같이 고정된 객체 레이아웃(클래스)와 유사하게 동작

변경 가능한 값

// 할당이 이뤄지는 시점에 객체 리터럴이 해석되고 객체가 생성됨
var person = {
  name: 'Lee'
}

// person 변수에 저장되어 있는 참조 값으로 실제 객체에 접근함
console.log(person); // {name: "Lee"}

// 프로퍼티 값 갱신
person.name = 'Kim';

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

console.log(person); // {name: "Kim", address: "Seoul"}
js_4
  • 객체를 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면 참조 값에 접근할 수 있음
  • 메모리에 저장되어 있는 참조 값을 통해 실제 객체에 접근함
원시 값객체
값 할당 표현변수는 O값을 갖는다변수는 객체를 참조하고 있다
값 변경 방법변경 불가능(재할당)변경 가능(재할당 없이 프로퍼티 추가 및 값 갱신, 삭제 가능)

객체를 생성하고 관리하는 방식은 매우 복잡하고 비용이 많이 드는 일이므로, 메모리를 효율적으로 사용하기 위해서 변경 가능한 값으로 설계됨
👉 구조적 단점에 따른 부작용 발생(여러 개의 식별자가 하나의 객체를 공유할 수 있음)

참조에 의한 전달

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"}

  • 객체를 가리키는 변수(원본, person)를 다른 변수(사본, copy)에 할당하면 원본의 참조 값이 복사되어 전달됨
js_5
  • 그림에서 보듯이, 원본 person과 사본 copy는 저장된 메모리 주소는 다르지만 동일한 참조 값을 가짐
    • 두 개의 식별자가 하나의 객체를 공유
    • 어느 한쪽에서 변경할 경우 서로 영향을 주고 받음

값에 의한 전달과 참조에 의한 전달은 메모리 공간에 저장되어 있는 값을 복사해서 전달한다는 면에서는 동일하나 변수에 저장되어 있는 값이 원시 값인지 참조 값인지에 따라 차이만 존재
👉 자바스크립트에서는 참조에 의한 전달은 존재하지 않고 값에 의한 전달만 존재함
(⚠️ ECMAScript 사양의 공식적인 용어는 아니므로 타언어에서의 참조에 의한 전달과 의미가 동일하지 않음)

Referenced

  • 이응모, 『모던 자바스크립트 Deep Dive』, 위키북스(2022.4.25), 137 ~ 153p