자바스크립트의 데이터타입

코어자바스크립트 정복 01

Posted by Juri on April 14, 2022

책 코어자바스크립트 (위키북스 출판) 을 참고해 작성한 포스팅입니다.

데이터 타입

  • 기본형 (primitive type) : 숫자, 문자열, 불리언, null, undefined 등
  • 참조형 (reference type) : 객체, 배열, 함수, 날짜, 정규표현식 등

기본형은 값이 담긴 주솟값을 바로 복제하는 반면에 참조형은 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제한다.

기본형 데이터와 참조형 데이터

불변값

1
2
3
4
5
6
var a = "abc";
a = a + "def";

var b = 5;
var c = 5;
b = 7;

변수 a에 문자열 ‘abc’를 할당했다가 뒤에 ‘def’를 추가하면 기존의 ‘abc’가 ‘abcdef’로 바뀌는 것이 아니라 새로운 문자열 ‘abcdef’를 만들어 그 주소를 변수 a에 저장한다. ‘abc’와 ‘abcdef’는 별개의 데이터로 볼 수 있다. 값의 변경은 데이터를 새로 만드는 동작을 통해서만 이루어진다. 이것이 불변값의 성질이다. 한 번 만들어진 값은 GC되지 않는 한 변하지 않는다.

가변값

1
2
3
4
var obj = {
    a: 1,
    b: "abc",
};

  1. 변수 영역의 빈 공간 (@1002)를 확보하고 그 주소의 이름을 obj로 지정한다.
  2. 임의의 데이터 공간 (@5001)에 여러 개의 프로퍼티로 이뤄진 데이터 그룹을 저장하기 전에 그룹 내부의 프로퍼티들을 저장하기 위해 별도의 변수 영역을 마련하고 그 영역의 주소 (@7103~7104)를 @5001에 저장한다.
  3. @7103과 @7104에 각각 a, b 라는 프로퍼티 이름을 지정한다.
  4. 데이터 영역에서 1을 검색한다. 검색 결과가 없으므로 임의의 데이터 공간 (@5003)에 저장하고 이 주소를 @7103에 저장한다. abc도 마찬가지로 실행한다.

기본형 데이터와 객체의 변수 영역이 별도로 존재한다는 차이점이 있다.

1
2
3
4
5
var obj = {
    a: 1,
    b: "abc",
};
obj.a = 2;

obj의 a프로퍼티에 2를 할당하려고 한다.

데이터 영역에 2를 검색한다. 검색 결과가 없으므로 임의의 데이터 공간(@5005)에 저장하고 이 주소를 @7103에 저장한다.

변수 obj이 바라보고 있는 주소는 @5001로 변하지 않는다. 새로운 객체가 만들어진 것이 아니라 기존의 객체 내부의 값이 바뀐 것이다.

변수 복사 비교

기본형 데이터와 참조형 데이터의 차이를 확인해보자.

1
2
3
4
5
var a = 10;
var b = a;

var obj1 = { c: 10, d: "abc" };
var obj2 = obj1;

기본형 데이터

  1. 변수 영역의 빈 공간 @1001을 확보하고 식별자로 a를 지정한다.
  2. 데이터 영역에 10이 없으므로 빈 공간 @5001에 저장하고 @1001에 이 주소를 넣는다.
  3. 변수 영역의 빈 공간 @1002를 확보하고 식별자로 b를 지정한다.
  4. 식별자 a를 검색해 @1001에 저장된 값인 @5001을 @1002에 값으로 대입한다.

참조형 데이터

  1. 변수 영역의 빈 공간 @1003을 확보하고 식별자로 obj1를 지정한다.
  2. 데이터 영역의 빈 공간 @5002를 확보하고 데이터 그룹을 저장해야하기 때문에 별도의 변수 영역을 마련하고 그 영역의 주소를 @5002에 저장한다.
  3. @7103과 @7104에 각각 c, d 라는 프로퍼티 이름을 지정한다.
  4. 데이터 영역에서 10을 검색한다. 검색 결과가 없으므로 임의의 데이터 공간 (@5003)에 저장하고 이 주소를 @7103에 저장한다. abc도 마찬가지로 실행한다.
  5. 변수 영역의 빈 공간 @1004를 확보하고 식별자로 obj2를 지정한다. 식별자 obj1을 검색해 @1003에 저장된 값인 @5002를 @1004에 값으로 대입한다.

변수를 복사하는 과정은 같은 주소를 바라보게 되는 점에서 동일하다.

객체의 프로퍼티를 변경하거나 객체 자체를 변경하면 큰 차이가 발생한다.

1
2
3
4
5
6
7
8
var a = 10;
var b = a;

var obj1 = { c: 10, d: "abc" };
var obj2 = obj1;

b = 15;
obj2.c = 20;

  1. 앞 부분은 위의 다른 예시와 같다.
  2. 데이터 영역에서 15를 검색한다. 검색 결과가 없으므로 새로운 공간 (@5004)에 저장하고 그 주소를 든 채로 변수 영역에서 식별자가 b인 주소를 찾는다. @1002의 값이 @5004로 바뀐다.
  3. 데이터 영역에서 20을 검색한다. 검색 결과가 없으므로 새로운 공간 (@5005)에 저장하고 그 주소를 든 채로 변수 영역에서 obj2를 찾는다. obj2의 값(@5002)이 가리키는 변수 영역에서 c를 찾아 그곳에 @5005를 대입한다.

기본형 데이터를 복사한 변수 b의 값을 바꿨더니 @1002의 값이 달라진 것과 다르게, 참조형 데이터를 복사한 변수 obj2의 프로퍼티 값을 바꿨더니 @1004의 값은 달라지지 않았다. 즉, 변수 a와 b는 다른 주소를 바라보게 됐으나, 변수 obj1와 obj2는 같은 객체를 바라보고 있다.

기본형은 주솟값을 복사하는 과정이 한 번 이뤄지고 참조형은 한 단계를 더 거친다!

주의 ) 객체 자체를 변경할 때는 각각의 객체가 다른 주소를 바라보게 된다.

1
2
3
4
5
6
7
8
var a = 10;
var b = a;

var obj1 = { c: 10, d: "abc" };
var obj2 = obj1;

b = 15;
obj2 = { c: 20, d: "abc" };

불변 객체

객체의 내부 프로퍼티를 변경할 때 원본 데이터가 변형 되지 않도록 불변 객체를 사용한다.

기존 정보를 복사해서 새로운 객체를 반환하는 함수

1
2
3
4
5
6
7
var copyObject = (target) => {
    var result = {};
    for (var prop in target) {
        result[prop] = target[prop];
    }
    return result;
};

result 객체에 target 객체의 프로퍼티를 복사한다.

copyObject를 이용한 객체 복사

1
2
3
4
5
6
7
var user = {
    name: "Juri",
    gender: "female",
};

var user2 = copyObject(user);
user2.name = "Jun";

user2 내부의 프로퍼티를 변경해도 원본 데이터인 user의 내부 프로퍼티는 바뀌지 않는다. copyObject를 이용하면 간단하게 객체를 복사할 수 있지만 얕은 복사만을 수행한다.

얕은 복사와 깊은 복사

얕은 복사(shallow copy)는 바로 아래 단계의 값만 복사하는 방법이고 깊은 복사(deep copy)는 내부의 모든 값들을 하나하나 찾아 전부 복사하는 방법이다. 얕은 복사는 중접된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 주소값만 복사한다는 뜻이다. 그러면 해당 프로퍼티에 대해 원본과 사본이 모두 동일한 참조형 데이터의 주소를 가리키게 된다. 사본을 바꾸면 원본이 바뀌고 원본이 바뀌면 사본도 바뀐다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var user = {
    name: "Juri",
    meal: {
        lunch: "yogurt",
        dinner: "pizza",
    },
};

var user2 = copyObject(user);

user2.name = "Jun";
console.log(user.name === user2.name); //false

user.meal.lunch = "sandwich";
console.log(user.meal.lunch === user2.meal.lunch); //true

user 객체에 직접 속한 프로퍼티에 대해서는 복사해서 완전히 새로운 데이터가 만들어진 반면에 한단계 더 들어간 내부 meal의 내부 프로퍼티들은 기존 데이터를 그대로 참조하고 있다.

1
2
3
4
5
var user2 = copyObject(user);
user2.meal = copyObject(user.meal);

user.meal.lunch = "pasta";
console.log(user.meal.lunch === user2.meal.lunch); //false

내부 프로퍼티인 meal에 copyObject함수를 실행한 결과를 할당했다.