자바스크립트 데이터 타입
기본형(number, string, boolean, null, undefined, symbol)과
참조형(object, array, function, date, regexp, Map, WeakMap, Set, WeakSet)이 존재함.

변수는 변할수있는 수
식별자 변수의 이름.
변수를 선언
let a;
변수 데이터 할당
a = 'Hello World';
이렇게 진행할 경우 메모리영역에서 아래와 같이 진행된다.
변수 주소 | 1000 | 1001 | 1002 | 1003 |
데이터 | 이름 : a 값 : @3000 |
데이터 주소 | 3000 | 3001 | 3002 | 3003 |
데이터 | "Hello World" |
위와 같이 데이터영역과 변수 영역이 나누어진 이유는
메모리의 낭비를 줄이기 위함과 가변적 메모리 길이에 대응하기 위함
위의 내용처럼 변수 영역 테이블이 기존 데이터 공간값이 고정이라면, 그보다 크게 들어올 경우 저장공간을 늘리도록 수정해야한다.
이때 마지막의 값이면 쉽게 늘리지만, 중간 저장값이라면 뒷 저장값을 모두 이동시켜야한다.
그렇기에 그냥 변수와 데이터를 분리하여 새로 저장해버리고 해당 값에 매칭시키는 게 연산에 이점을 준다.
변수 주소 | 1000 | 1001 | 1002 | 1003 |
데이터 | 이름 : a 값 : @3001 |
|||
데이터 주소 | 3000 | 3001 | 3002 | 3003 |
데이터 | "Hello World" | "Hello World 123" |
또한 값이 중복되는 경우가 있는데,
변수 영역 1001의 값이 3001을 보고 있다. 만약 변수 400개가 "Hello World 123"을 표시해야할 경우
해당 변수 메모리에서 각자 값을 모두 가지고 있다면 그만큼 동일한 내용을 가져야해서 해당 메모리자원이 낭비될 것이다.
이를 예방하는게 데이터 영역으로 분리되어 동일한 @3001을 가지므로 사용자가 보기엔 값이 분리되었지만,
실질적으로 동일한 데이터를 보여지므로 한정된 컴퓨터 자원을 효율적으로 사용하게 된다.
불변성
상수와 변수의 차이는 변수영역의 변경가능성이 차이점이다.
상수와 불변값은 다르다.
그렇다면 불변값의 불변성은 뭘까?
바로 "데이터 영역"의 변경여부다.
let a = 'abc';
a = a+ 'def';
let b = 1;
let c = 1;
b = 3;
- a의 데이터는 'abc'를 메모리에 할당했다.
- 컴퓨터는 'abcdef'가 저장되어 있는지 확인한다. 해당 데이터가 없으면
- 컴퓨터는 'abcdef'를 새롭게 메모리에 할당한다.
- 기존 a != 신규 a의 메모리값은 다르다.
- b의 값 1을 메모리에 할당한다.
- c의 값 1은 기존 메모리에 있어 이를 재사용한다
- b의값을 3을 할당하려고 하는데, 없으니 3을 메모리에 집어넣는다.
위 사례를 보아, 기존 저장된 값은 메모리에 삭제되지 않고 그대로 유지되어 있다.
이를 불변성이라고 한다.
그렇다면, 불변값의 반댓말인 가변값이 있을 것 같지 않나?
가변값은 뭘까?
변할 수 있는 값이다.
기본형 데이터는 불변값이다.
참조형 데이터는 모두 가변값일까? 아니다. 불변값으로 설정할 수도 있고, 가변값으로 설정할 수 있다.
let a = {
b : 1,
c: 'ab'
}
변수 주소 | 1000 | 1001 | 1002 | 1003 |
데이터 | 이름 : a 값 : @4000 |
|||
데이터 주소 | 4000 | 4001 | 4002 | 4003 |
데이터 | @2000 ~ ? | 1 | 'ab' |
객체 데이터 주소 | 2000 | 2001 | 2002 | 2003 |
데이터 | 이름 : b, 값 : @4002 |
이름 : c, 값 : @4003 |
기본형 데이터는 객체 주소(4000)를 참조하고 있다.
해당 값은 불변성은 유지된다.
데이터 값은 4002, 4003은 할당되어 유지되고 있다.
let a = {
b : 1,
c: 'ab'
}
a.b = 2;
객체 a에 포함된 변수 b를 새롭게 데이터를 2를 할당하여, 넣으려보니 없어 컴퓨터는 메모리 주소 4004에 2를 할당했다.
그런데, 객체 a의 값은 그대로 4000을 유지되어 변경되지 않고 있다.
바뀐건 객체테이블 데이터주소 값이다.
해당 값은 가변값(immutable)이 되어 변경된 것.
이를 가변값이라고 한다.
변수 주소 | 1000 | 1001 | 1002 | 1003 | 1004 |
데이터 | 이름 : a 값 : @4000 |
||||
데이터 주소 | 4000 | 4001 | 4002 | 4003 | 4004 |
데이터 | @2000 ~ ? | 1 | 'ab' | 2 |
객체 데이터 주소 | 2000 | 2001 | 2002 | 2003 |
데이터 | 이름 : b, 값 : @4004 |
이름 : c, 값 : @4003 |
let a = {
b : 1,
arr: [1,2,3]
}
간혹 위처럼 객체가 구성되어 있는데, 이를 중첩객체라고 부른다.
변수 주소 | 1000 | 1001 | 1002 | 1003 | 1004 |
데이터 | 이름 : a 값 : @4000 |
||||
데이터 주소 | 4000 | 4001 | 4002 | 4003 | 4004 |
데이터 | @2000 ~ ? | @3000 ~ ? | 1 | 3 | 2 |
객체 데이터 주소 | 2000 | 2001 | 2002 | 2003 |
데이터 | 이름 : b, 값 : @4002 |
이름 : arr, 값 : @4001 |
객체 데이터 주소 | 3000 | 3001 | 3002 | 3003 |
데이터 | 이름 : 1, 값 : @4002 |
이름 : 2, 값 : @4004 |
이름 : 3, 값 : @4003 |
해당 내용 처럼 중첩객체에 따라 객체 테이블이 늘어나면서 참조 테이블이 증가하게 된다.
그런데, 이렇게 증가하다보면, 데이터가 사용되면서 변경되어 참조테이블이 사라지면서 0이 되는 게 있다.
이때 가비지컬렉터가 메모리가 부족할 경우 자동으로 수거해서 빈공간이 된다.
그렇다면 객체는 무조건 가변값인건가? 아니다.
let a = {
b : 1,
c: 'ab'
}
let d = a;
consoloe.log(a === d); // true
let a = {
b : 1,
c: 'ab'
}
let d = {
b : 1,
c: 'ab'
}
consoloe.log(a === d); // false
해당 차이는 왜 발생한걸까?
답은 1번의 경우 동일한 메모리 값을 참조하지만, 2번의 경우 새롭게 메모리주소에 할당했기때문이다.
사람이 보기엔 동일한 값이지만,
객체의 참조 테이블 주소 값이 같은 기본형데이터를 바라보고 있지만 변수영역의 기본형 데이터값은 다른 주소로 할당되어 그렇다
막상 글자로 보면 이해가 되지 않는데,
테이블로 표현하면 아래와 같다.
변수 주소 | 1000 | 1001 | 1002 | 1003 | 1004 |
데이터 | 이름 : a 값 : @4000 |
이름 : d 값 : @4000 |
|||
데이터 주소 | 4000 | 4001 | 4002 | 4003 | 4004 |
데이터 | @2000 ~ ? | 1 | 'ab' |
객체 데이터 주소 | 2000 | 2001 | 2002 | 2003 |
데이터 | 이름 : b, 값 : @4002 |
이름 : c, 값 : @4001 |
위의 내용이 1번의 내용.
변수 주소 | 1000 | 1001 | 1002 | 1003 | 1004 |
데이터 | 이름 : a 값 : @4000 |
이름 : d 값 : @4003 |
|||
데이터 주소 | 4000 | 4001 | 4002 | 4003 | 4004 |
데이터 | @2000 ~ ? | 1 | 'ab' | @3000 ~ ? |
객체 데이터 주소 | 2000 | 2001 | 2002 | 2003 |
데이터 | 이름 : b, 값 : @4002 |
이름 : c, 값 : @4001 |
객체 데이터 주소 | 3000 | 3001 | 3002 | 3003 |
데이터 | 이름 : b, 값 : @4002 |
이름 : c, 값 : @4001 |
위의 내용은 2번의 내용을 보고 만든 테이블이다.
즉, 가변성은 객체 내부에 존재하는 프로퍼티(속성 값)을 수정할때만 유지된다.
그렇다면, 뭐든 복사할때 주소값도 같이 복사되는걸까?
아니다.
얕은복사 vs 깊은복사가 있다.
얕은복사는 바로 아래값만 복사한다. 중첩객체는 그냥 값만 복사해간다.
그에 비해서 깊은 복사는 그 중첩객체 내 모든 값을 복사한다.
쉽게 말하자면 복사 행위라서 헷깔리는건데,
얕은 복사사할지라도 새롭게 object를 반환하면 새로운 주소를 할당하지만 중첩객체 메모리 주소는 그대로 유지되고,
깊은 복사를 진행할 경우 중첩된 객체주소도 새롭게 할당하여 진행하게 된다.
let a = {
name: '홍길동',
profile: {
address: 'seoul',
gender: 'man',
agreement: false
}
}
let b = a;
b.name = '변사또';
b.profile.gender = 'female';
console.log(b.name === a.name); /// true
console.log(b.profile.gender === a.profile.gender); /// true
console.log(a.profile.gender); // female
console.log(b.profile.gender); // female
// b값을 변경했는데, a값도 변경되었다. 이러면 불변성이 깨져버린 것.
// 얕은 복사 상태이다.
let a = {
name: '홍길동',
profile: {
address: 'seoul',
gender: 'man',
agreement: false
}
}
let b = structuredClone(a); // 깊은 복사
b.name = '변사또';
b.profile.gender = 'female';
console.log(b.profile.gender === a.profile.gender) // false
console.log(b.name === a.name); // false
console.log(a.name); // '홍길동'
console.log(b.name); // '변사또'
undefined
2가지의 출력조건이 있다.
1. 명시적 표시를 하지 않은 경우.
- 값을 대입하지 않은 변수에 접근
- 존재하지 않은 프로퍼티에 접근
- 반환 값이 없는 함수를 실행할 경우
2. 사용자가 고의적으로 직접 할당한 경우
foreach, map, filter, reduce의 경우 예외없이 출력된다.
그러나 명시하지 않은 경우 이를 건너뛰거나 출력하기도 한다.
위 2가지의 경우가 있어 어떻게 사용해야하나 싶은데, 고의적인 명시하는 방법을 쓰지 않으면 된다.
이를 위해 null값이 있다.
null은 비어있는 값으로 해당 값을 명시한 것으로 위에서 말한 배열함수를 건너뛰게한다.
대신 주의점이 있는데, null의 타입이 object라는 것이다.
이는 자바스크립트 버그.
그렇기에 === 일치연산자(타입체크 하는 비교연산자)를 사용해야한다.
'개발공부 > BOOKS' 카테고리의 다른 글
[독서] 코어자바스크립트 3장 (0) | 2025.03.19 |
---|---|
[독서] 코어 자바스크립트(2장) (0) | 2025.03.17 |