이 글은 코어자바스크립트 1장을 읽고 정리한 글입니다.
데이터 타입
데이터 타입은 크게 기본형과 참조형으로 나누어져 있다.
기본형과 참조형 모두 복제
-> 기본형은 값이 담긴 주솟값을 바로 복제
-> 참조형은 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제
메모리와 데이터
컴퓨터는 모든 데이터를 0 또는 1로 표현
비트 - 0 또는 1로 표현할 수 있는 하나의 메모리 조각
메모리는 수많은 비트들로 구성되어 있는데 각 비트는 고유한 식별자를 통해 위치를 확인할 수 있다.
수많은 비트들을 몇 개씩 묶어 하나의 단위로 나타내면 표현할 수 있는 값도 늘어나고 동시에 검색 시간을 줄일 수 있다.
바이트 - 8개의 비트로 구성 (총 2^8, 256개)
자바, C/C++과 같은 정적 타입 언어는 메모리 낭비를 최소화하기 위해 데이터 타입별로 메모리 영역이 정해져 있어 형변환이 필요했지만 자바스크립트는 메모리 관리가 자유로워 형변환이 크게 필요없다.
식별자와 변수
변수는 변경 가능한 데이터가 담길 수 있는 공간 또는 그릇
식별자는 데이터를 식별하는 데 사용하는 이름, 즉 변수명
변수 선언
var a;
<변수 선언에 대한 메모리 영역의 변화>
컴퓨터는 메모리에서 비어있는 공간 하나를 확보한다. (1003번)
이 공간에 이름(식별자)를 a로 지정한다.
데이터 할당
var a; // 변수 a 선언
a = 'abc'; // 변수 a에 대한 데이터 할당
var a = 'abc' // 변수 선언과 동시에 데이터 할당
<데이터 할당에 대한 메모리 영역의 변화>
다시 말해, 변수 영역에 저장된 주소에 데이터를 할당하지 않는다!
💡 왜 데이터를 변수 영역에 직접 대입하고 않고 데이터 영역에 따로 저장하는가?
- 데이터 변환을 자유롭게 할 수 있고 메모리를 효율적으로 관리하기 위함
- 만약 직접 대입하면, 데이터 변환 시 확보된 공간을 늘리는 작업이 필요하다.
- 여유 공간이 없다면 데이터를 전부 옮기고 이동시킨 주소를 각 식별자에 다시 연결하는 등 처리해야할 연산량이 너무 많아진다.
🗸 따라서 변수와 데이터를 각각의 공간으로 저장하는 것이 데이터의 변환을 처리할 때, 최적이다!
데이터 변환
var a = 'abc'
a = 'abcdef';
<문자열 변환에 대한 메모리 영역의 변화>
불변값
var a = 'abc';
a = a + 'def';
var b = 5;
var c = 5;
b = 7;
문자열 값도 숫자도 다른 값으로 변경할 수 없다. 여기서 변경은 새로 만드는 동작을 통해서만 이루어진다.
가변값
var obj1 = {
a: 1,
b: 'bbb'
};
💡 기본형 데이터와의 차이는?
-> 객체의 변수인 프로퍼티 영역이 별도로 존재한다는 점이다!
데이터 영역에 저장된 값은 모두 불변값(숫자, 문자열)이지만 변수에는 다른 값을 얼마든지 대입할 수 있기 때문에 불변하지 않다. (= 가변적이다)
참조형 데이터의 프로퍼티를 재할당하면 어떻게 될까?
var obj1 = {
a: 1,
b: 'bbb'
};
obj1.a = 2;
obj1은 변하지 않고, obj1의 프로퍼티 a가 저장된 주소값만 5004에서 5003으로 교체된다.
참조형 데이터의 프로퍼티에 다시 참조형 데이터를 할당하는 경우를 중첩객체 라고 한다.
var obj = {
x: 3,
arr: [3, 4, 5]
};
새로 추가된 과정은 먼저,
만약 중첩 객체의 프로퍼티를 재할당한다면 어떻게 될까?
어떤 데이터에 대해 자신의 주소를 참조하는 변수의 개수를 참조 카운트라고 한다.
해당 그림에서 @5003, @8104 ~ 는 참조 카운트가 1이었다가 0이 된다. 참조 카운트가 0인 메모리 주소를 가비지 컬렉터(Garbage Collector, GC)로 수거대상이 된다. 수거 대상이 된 가비지 컬렉터는 런타임 환경에 따라 특정 시점이나 메모리 사용량이 포화 상태에 임박할 때마다 자동으로 수거 대상들을 수거한다. 수거된 메모리는 다시 빈 공간이 되어 새로운 값을 할당받을 수 있다.
변수 복사 비교
<변수 복사>
var a = 10;
var b = a;
var obj1 = {
c: 10,
d: 'ddd'
};
var obj2 = obj1;
앞서 본 과정과 마찬가지로 각각 변수 영역의 빈 공간을 확보하고 a, b를 지정한다. 둘은 각각의 공간을 가지고 같은 주솟값을 갖는다.
그리고 참조형 데이터는 ob1, obj2에 해당하는 각각의 변수 영역의 빈 공간을 확보하고 데이터 영역의 묶음 주소는 같은 주솟값을 갖는다.
<객체의 프로퍼티 변경>
var a = 10;
var b = a;
var obj1 = {
c: 10,
d: 'ddd'
};
var obj2 = obj1;
b = 15;
obj2.c = 20;
반면 다음과 같이 객체의 프로퍼티가를 변경했을 때에는 다르게 동작한다.
기본형 데이터를 복사한 변수는 b는 값이 달라져 서로 다른 주솟값을 가진다. 반면 obj1과 obj2는 프로퍼티가 저장된 메모리의 값만 바뀌어 메무리 주솟값만 변경될 뿐 ojb1과 obj2는 같은 주솟값을 가진다.
<객체 자체를 변경했을 때>
var a = 10;
var b = a;
var obj1 = {
c: 10,
d: 'ddd'
};
var obj2 = obj1;
b = 15;
obj2 = {
c: 20,
d: 'ddd'
}
이와 같이 객체 자체를 변경했을 때에는 객체를 새롭게 만들었기 때문에 obj1 !== obj2가 성립한다.
💡 참조형 데이터의 '가변값'이 언제를 의미할까?
'가변'은 참조형 데이터 자체를 변경했을 때가 아닌 내부의 프로퍼티를 변경했을 때만 성립 가능하다.
참조형 데이터는 '가변적'이라고 하지만 데이터 자체를 변경한다면 기존 데이터는 변경하지 않는 불변성을 확보할 수 있다.
값으로 전달받은 객체에 변경에 변경을 가하더라도 원본 객체는 변하지 않아야 할 때 -> 불변 객체 필요!
얕은 복사
깊은 복사
'없음'을 나타내는 값으로 같지만 undefined와 null은 미세하게 다르고 사용하는 목적 또한 다르다.
undefined
언제 undefined와 null을 사용할까?
typeof null은 object로 어떤 변수의 값이 null인지의 여부를 판별하기 위해 typeof는 적절하지 않다.
var n = null;
console.log(typeof n); // object
console.log(n == null) // true
console.log(n === null) // true
console.log(n == undefined) // true
console.log(n === undefined) // false
null과 undefined를 비교하기 위해서는 동등 연산자(==)로 비교할 경우 true 값을 반환하기 때문에 일치 연산자(===)를 사용하여 판별해야한다.