일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
Tags
- 10926번
- 실행 컨텍스트
- v-on
- 백준
- vue
- 도커
- Python
- 3003번
- 빅오표기법
- JavaScript
- 젠킨스
- v-if
- hoisting
- 우테코
- MSA
- 쿠버네티스
- v-for
- v-model
- 이벤트캡쳐링
- 이벤트버블링
- 코어자바스크립트
- 파이썬
- 리스트복사
- 프리코스
- 10869번
- DevOps
- 객체지향의 사실과 오해
- 2588번
- LeetCode
- 배열파티션
Archives
- Today
- Total
새오의 개발 기록
코어 자바스크립트: 1. 데이터 타입 본문
01. 데이터 타입
1. 데이터 타입의 종류
자바스크립트의 데이터 타입은 크게 두 가지로 나뉨
데이터 타입의 종류
- 기본형
- Number
- String
- Boolean
- null
- undefined
- Symbol(es6에 추가)
- 참조형
- object
- Array
- Function
- Date
- RegExp
- Map, WeakMap
- Set, WeakSet
- object
데이터 타입의 특징
- 기본형
- 할당/연산 시 복제된다.
- 값이 담긴 주솟값을 바로 복제
- 불변성(immutability)를 띈다.
- 참조형
- 할당/연산 시 참조된다.
- 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제
2. 데이터 타입에 관한 배경 지식
메모리와 데이터
- 1바이트는 8개의 비트로 구성, 1비트는 0 또는 1의 두 가지 값을 표현
- 모든 데이터는 바이트 단위의 식별자, 즉 '메모리 주솟값'을 통해 서로 구분하고 연결
식별자와 변수
- 변수: variable. 변할 수 있는 데이터
- 식별자: 어떤 데이터를 식별하는 데 사용하는 이름. 즉 변수명을 의미
3. 변수 선언과 데이터 할당
변수 선언
var a; // 변할 수 있는 데이터를 만든다. 이 데이터의 식별자는 a 로 한다.
변수 선언에 대한 메모리 영역의 변화
주소 | ... | 1002 | 1003 | 1004 | 1005 | ... |
데이터 | 이름: a 값: |
- 메모리에서 비어있는 공간 하나를 확보한다.(1003번)
- 이 공간의 이름(식별자)를 a 라고 지정한다.
데이터 할당
var a; // 변수 a 선언
a = 'abc'; // 변수 a에 데이터 할당
var a = 'abc'; // 변수 선언과 할당을 한 문장으로 표현
데이터 할당에 대한 메모리 영역의 변화
변수 영역 |
주소 | ... | 1002 | 1003 | 1004 | 1005 | ... |
데이터 | 이름: a 값: @5004 |
||||||
데이터 영역 |
주소 | 5002 | 5003 | 5004 | 5005 | ... | |
데이터 | 'abc' |
- 변수 영역 메모리에서 비어있는 공간 하나를 확보한다.(1003번)
- 이 공간의 이름(식별자)를 a 라고 지정한다.
- 데이터 영역의 빈 공간(@5004)에 문자열 'abc'를 저장한다.
- 변수 영역에서 a라는 식별자를 검색한다(@1003)
- 앞서 저장한 문자열의 주소(@5004)를 @1003의 공간에 대입한다.
❓변수 영역에 값을 직접 대입하지 않고 굳이 한 단계를 더 거치는 이유는?
- 데이터 변환을 자유롭게 할 수 있게 함과 동시에 메모리를 더 효율적으로 관리
- 자바스크립트는 숫자형 데이터에 64비트(8바이트)의 공간을 확보하는데 문자열은 정해진 규격이 없음. 메모리 용량이 가변적이므로 확보된 공간을 변환된 데이터 크기에 맞게 늘리는 작업이 필요 없음
- 변수와 데이터를 별도의 공간에 나누어 저장하면 효율적으로 문자열 데이터의 변환을 처리할 수 있음
- 문자열 'abc'의 마지막에 'def'를 추가하라고 하면 컴퓨터는 앞서 'abc'가 저장된 공간에 'abcdef'를 할당하는 대신 'abcdef'라는 문자열을 새로 만들어 별도의 공간에 저장하고 그 주소를 변수 공간에 연결함
변수 영역 |
주소 | ... | 1002 | 1003 | 1004 | 1005 | ... |
데이터 | 이름: a 값: @5005 |
||||||
데이터 영역 |
주소 | 5002 | 5003 | 5004 | 5005 | ... | |
데이터 | 'abc' | 'abcdef' |
4. 기본형 데이터와 참조형 데이터
불변값
var a = 'abc'; // 변수 a에 문자열 'abc' 할당
a = a + 'def'; // 변수 a에 문자열 'abcdef' 재할당(데이터 영역에 새로 만들어 할당함)
var b = 5; // 변수 b에 숫자 5 할당(데이터 영역에서 5를 찾고 없으면 데이터 공간을 만들어 저장)
var c = 5; // 4번째 줄에서 이미 데이터 영역에 5 만들어 놨으므로 재활용 해서 할당
b = 7; // 데이터 공간에 7은 없으므로 새로 만들어서 재할당
- 변수와 상수를 구분하는 성질은 변경 가능성
- 변경 가능성의 대상은 변수 영역 메모리. 한 번 데이터 할당이 이뤄진 변수 공간에 다른 데이터를 재할당할 수 있는지 여부가 관건
- 불변값과 상수는 같은 개념이 아님
- 불변성 여부를 구분할 때의 변경 가능성의 대상은 데이터 영역 메모리
- 예시에서 2번째 줄에서 한 번 만든 값을 변경하는게 아니라 데이터 영역에 새로 만든 이유가 불변값의 성질 때문임
- 한 번 만들어진 값은 가비지 컬렉팅을 당하지 않는 이상 영원히 변하지 않음.
- 새로 만드는 동작을 통해서만 변경 가능
- 기본형 데이터(Number, String, boolean, null, undefined, Symbol)은 모두 불변값
가변값
- 참조형 데이터는 기본적으로 가변값인 경우가 많음
참조형 데이터의 할당
var obj1 = {
a: 1,
b: 'bbb'
};
변수 영역 |
주소 | 1001 | 1002 | 1003 | 1004 |
데이터 | 이름: obj1 값: @5001 |
||||
데이터 영역 |
주소 | 5001 | 5002 | 5003 | 5004 |
데이터 | @7103 ~ ? | 1 | 'bbb' |
객체 @5001의 변수 영역 |
주소 | 7103 | 7104 | 7105 | 7106 |
데이터 | 이름: a 값: @5003 |
이름: b 값: @5004 |
- 변수 영역의 빈 공간(@1002)을 확보하고, 그 주소의 이름을 obj1로 정합니다.
- 임의의 데이터 저장 공간(@5001)에 데이터를 저장하려고 보니 여러 개의 프로퍼티로 이뤄진 데이터 그룹이라 별도의 변수 영역을 마련하고, 그 영역의 주소(@7103 ~ ?)을 @5001에 저장
- @7103 및 @7104에 각각 a와 b라는 프로퍼티 이름을 저장
- 데이터 영역에서 숫자 1을 검색. 검색 결과가 없으므로 임의로 @5003에 저장하고, 이 주소를 @7103에 저장. 문자열 'bbb'역시 임의로 @5004에 저장하고, 이 주소를 @7104에 저장
- 객체의 변수(프로퍼티)의 영역이 별도로 존재한다는 점에서 기본형 데이터와 차이가 있음
- 객체가 별도로 할애한 영역은 변수 영역일 뿐 '데이터 영역'은 기존의 메모리 공간을 그대로 활용하고 있음
- 데이터 영역은 불변값이나 변수는 변경 가능해서 가변값이라고 함.
참조형 데이터의 재할당
var obj1 = {
a: 1,
b: 'bbb'
};
obj1.a = 2;
변수 영역 |
주소 | 1001 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름: obj1 값: @5001 |
|||||
데이터 영역 |
주소 | 5001 | 5002 | 5003 | 5004 | 5005 |
데이터 | @7103 ~ ? | 1 | 'bbb' | 2 |
객체 @5001의 변수 영역 |
주소 | 7103 | 7104 | 7105 | 7106 |
데이터 | 이름: a 값: @5005 |
이름: b 값: @5004 |
- 5번째 줄 데이터 영역에 숫자 2가 없으므로 빈 공간인 @5005에 저장하고, 이 주소를 @7103에 저장
- 변수 obj1이 바라보고 있는 주소는 @5001로 변하지 않았음
- 새로운 객체가 만들어진 것이 아니라 기존의 객체 내부의 값만 바뀐 것
중첩된 참조형 데이터(객체)의 프로퍼티 할당
var obj = {
x: 3,
arr: [3, 4, 5]
};
변수 영역 |
주소 | 1001 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름: obj1 값: @5001 |
|||||
데이터 영역 |
주소 | 5001 | 5002 | 5003 | 5004 | 5005 |
데이터 | @7103 ~ ? | 3 | @8104 ~ ? | 4 | 5 |
객체 @5001의 변수 영역 |
주소 | 7103 | 7104 | 7105 | 7106 |
데이터 | 이름: x 값: @5002 |
이름: arr 값: @5003 |
객체 @5003의 변수 영역 |
주소 | 8104 | 8105 | 8106 | |
데이터 | 이름: 0 값: @5002 |
이름: 1 값: @5004 |
이름: 2 값: @5005 |
- 변수 영역의 빈 공간(@1002)을 확보하고, 그 주소의 이름을 obj로 지정
- 임의의 데이터 저장공간(@5001)에 데이터를 저장하려는데, 이 데이터는 여러 개의 변수와 값들을 모아놓은 그룹(객체)임. 이 그룹의 각 변수(프로퍼티)들을 저장하기 위해 별도의 변수 영역을 마련하고(@7103 ~ ?), 그 영역의 주소를 (@5001)에 저장함
- @7103에 이름 x를, @7104에 이름 arr를 지정
- 데이터 영역에서 숫자 3을 검색. 없으므로 @5002에 저장하고 이 주소를 @7103에 저장
- @7104에 저장할 값은 배열로서 역시 데이터 그룹. 이 그룹 내부의 프로퍼티들을 저장하기 위해 별도의 변수 영역을 마련하고(@8104~?), 그 영역의 주소 정보를 @5003에 저장. @5003을 7104에 저장
- 배열의 요소가 총 3개이므로 3개의 변수 공간을 확보하고 각각 인덱스 부여.
- 데이터 영역에서 숫자 3을 검색해서(@5002) 그 주소를 @8104에 저장
- 데이터 영역에 숫자 4가 없으므로 @5004에 저장하고, 이 주소를 @8105에 저장
- 데이터 영역에 숫자 5가 없으므로 @5005에 저장하고, 이 주소를 @8106에 저장
- obj.arr[1]을 검색하는 경우
- obj 검색 1: obj라는 식별자를 가진 주소를 찾음(@1001)
- obj 검색 2: 값이 주소이므로 그 주소로 이동(@5001)
- obj 검색 3: 값이 주소이므로 그 주소로 이동(@7103~?)
- obj.arr 검색 1: arr이라는 식별자를 가진 주소를 찾음(@7104)
- obj.arr 검색 2: 값이 주소이므로 그 주소로 이동(@5003)
- obj.arr 검색 3: 값이 주소이므로 그 주소로 이동(@8104~?)
- obj.arr[1] 검색 1: 인덱스 1에 해당하는 주소를 찾음(@8105)
- obj.arr[1] 검색 2: 값이 주소이므로 그 주소로 이동
- obj.arr[1] 검색 3: 값이 숫자형 데이터이므로 4를 반환
obj.arr = 'str; // 재할당
- 여기서 재할당 하게 되면 참조 카운트가 0인 메모리 주소는 가비지 컬렉터가 수거함
- 참조 카운트: 어던 데이터에 대해 자신의 주소를 참조하는 변수의 개수
변수 영역 |
주소 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 |
데이터 | 이름: obj1 값: @5001 |
||||||
데이터 영역 |
주소 | 5001 | 5002 | 5003 | 5004 | 5005 | 5006 |
데이터 | @7103 ~ ? | 3 | @8104 ~ ? | 4 | 5 | 'str' |
객체 @5001의 변수 영역 |
주소 | 7103 | 7104 |
데이터 | 이름: x 값: @5002 |
이름: arr 값: @5003 |
객체 @5003의 변수 영역 |
주소 | 8104 | 8105 | 8106 |
데이터 | 이름: 0 값: @5002 |
이름: 1 값: @5004 |
이름: 2 값: @5005 |
변수 복사 비교
변수 복사
var a = 10;
var b = a;
var obj1 = { c: 10, d: 'ddd' } ;
var obj2 = obj1;
변수 영역 |
주소 | 1001 | 1002 | 1003 | 1004 |
데이터 | 이름: a 값: @5001 |
이름: b 값: @5001 |
이름: obj1 값: @5002 |
이름: obj2 값: @5002 |
|
데이터 영역 |
주소 | 5001 | 5002 | 5003 | 5004 |
데이터 | 10 | @7103 ~ ? | 'ddd' | 4 |
객체 @5002의 변수 영역 |
주소 | 7103 | 7104 |
데이터 | 이름: c 값: @5001 |
이름: d 값: @5003 |
- 기본형 데이터에 대한 변수 선언 및 할당
- 변수 영역의 빈 공간 @1001 확보하고 식별자를 a로 지정
- 숫자 10을 데이터 영역에서 검색하고 없으므로 빈 공간 @5001에 저장한 다음, 이 주소를 @1001에 넣음.
- 변수 복사
- 변수 영역의 빈 공간 @1002을 확보하고 식별자를 b로 지정
- 식별자 a를 검색해 값을 찾기 위해 @1001에 저장된 값인 @5001을 들고 와 @1002에 값으로 대입
- 참조형 데이터(객체)에 대한 변수 선언 및 할당
- 변수 영역의 빈 공간 @1003을 확보해 식별자를 obj1로 지정
- 데이터 영역의 빈 공간 @5002를 확보하고, 데이터 그룹이 담겨야 하기 때문에 별도의 변수 영역 @7103을 확보해 그 주소를 저장함
- @7103에 식별자 c를, @7104에 식별자 d를 입력한 다음, c에 대입할 값 10을 데이터 영역에서 검색해 연결
- 문자열 'ddd'는 데이터 영역의 빈 공간에 새로 만들어 @7104에 연결
- 변수 복사
- 변수 영역의 빈 공간 @1004를 확보하고 식별자를 obj2로 지정
- 식별자 obj1을 검색해(@1003) 그 값인 @5002를 들고, @1004에 값으로 대입
변수를 복사하는 과정은 기본형 데이터와 참조형 데이터 모두 같은 주소를 바라보게 되는 점에서 동일함
(@1001과 @1002는 모두 값이 @5001이 됐고, @1003과 @1004에는 모두 값이 @5002가 됐음)
복사 과정은 동일하지만 데이터 할당 과정에서 이미 차이가 있기 때문에 변수 복사 이후의 동작에 큰 차이 있음
변수 복사 이후 값 변경 결과 비교(1) - 객체의 프로퍼티 변경 시
var a = 10;
var b = a;
var obj1 = { c: 10, b: 'ddd' };
var obj2 = obj1;
b = 15;
obj2.c = 20;
변수 영역 |
주소 | 1001 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름: a 값: @5001 |
이름: b 값: @5004 |
이름: obj1 값: @5002 |
이름: obj2 값: @5002 |
||
데이터 영역 |
주소 | 5001 | 5002 | 5003 | 5004 | 5005 |
데이터 | 10 | @7103 ~ ? | 'ddd' | 15 | 20 |
객체 @5002의 변수 영역 |
주소 | 7103 | 7104 |
데이터 | 이름: c 값: @5005 |
이름: d 값: @5003 |
- 1~4번째 줄까지는 위의 코드와 같음
- 6번째 줄에서 데이터 영역에 15가 없으므로 새로운 공간 @5004에 저장하고, 그 주소를 든 채로 변수 영역에서 식별자가 b인 주소를 찾음
- @1002의 값이 @5004가 됨
- 7번째 줄에서 데이터 영역에 20이 없으므로 새로운 공간 @5005에 저장하고, 그 주소를 든 채로 변수 영역에서 obj2를 찾고, obj2의 값인 @5002가 가리키는 변수 영역에서 다시 c를 찾아(@7103) 그곳에 @5005를 대입함
변수 a와 b는 서로 다른 주소를 바라보게 됐으나, 변수 obj1과 obj2는 여전히 같은 객체를 바라보고 있는 상태
a !== b
obj1 === obj2
❓왜 이런 차이가 발생하는가?
기본형이든 참조형이든 변수에 할당하기 위해서는 주솟값을 복사해야하기 때문에 자바스크립트의 모든 데이터 타입은 참조형 데이터임
다만 기본형은 주솟값을 복사하는 과정이 한 번만 이뤄지고, 참조형은 한 단계를 더 거치게 된다는 차이가 있는 것.
변수 복사 이후 값 변경 결과 비교(2) - 객체 자체를 변경했을 때
var a = 10;
var b = a;
var obj1 = { c: 10, b: 'ddd' };
var obj2 = obj1;
b = 15;
obj2 = { c: 20, d: 'ddd' };
변수 영역 |
주소 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 |
데이터 | 이름: a 값: @5001 |
이름: b 값: @5004 |
이름: obj1 값: @5002 |
이름: obj2 값: @5006 |
|||
데이터 영역 |
주소 | 5001 | 5002 | 5003 | 5004 | 5005 | 5006 |
데이터 | 10 | @7103 ~ ? | 'ddd' | 15 | 20 | @8204 ~ ? |
객체 @5002의 변수 영역 |
주소 | 7103 | 7104 |
데이터 | 이름: c 값: @5005 |
이름: d 값: @5003 |
객체 @5006의 변수 영역 |
주소 | 8204 | 8205 |
데이터 | 이름: c 값: @5005 |
이름: d 값: @5003 |
- 프로퍼티를 변경했을 때(1)와 달리 객체 자체를 변경(2)한 경우에는 값이 달라짐(@5002 -> @5006)
즉, 참조형 데이터가 '가변값'이라고 설명할 때의 '가변'은 참조형 데이터 자체를 변경할 경우가 아니라 그 내부의 프로퍼티를 변경할 때만 성립
데이터 자체를 변경하고자 하면(새로운 데이터를 할당하고자 하면) 기본형 데이터와 마찬가지로 기존 데이터는 변하지 않음
5. 불변 객체
불변 객체를 만드는 간단한 방법
- 객체도 데이터 자체를 변경하고자 하면 기본형과 마찬가지로 기존 데이터는 변하지 않음(불변성), 내부 속성 변경 시만 변경(가변성)
- 따라서 내부 속성 변경시마다 매번 새로운 객체를 만들어 재할당하거나, 자동으로 새로운 객체를 만드는 도구를 활용하면 객체도 불변성을 확보할 수 있음
불변 객체가 필요한 상황
- 값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 경우
객체 가변성에 따른 문제점
var user = {
name: 'Jaenam',
gender: 'male',
};
var changeName = function (user, newName) {
var newUser = user;
newUser.name = newName;
return newUser;
};
var user2 = changeName(user, 'Jung');
if (user !== user2) {
console.log('유저 정보가 변경되었습니다.');
}
console.log(user.name, user2.name); // Jung Jung
console.log(user === user2); // true
객체의 가변성에 따른 문제점의 해결 방법
var user = {
name: 'Jaenam',
gender: 'male'
};
var changeName = function(user, newName) {
return {
name: newName,
gender: user.gender
}
};
var user2 = changeName(user, 'Jung');
if (user !== user2) {
console.log('유저 정보가 변경되었습니다.'); //유저 정보가 변경되었습니다.
};
console.log(user.name, user2.name); // Jaenam Jung
console.log(user === user2) // false
- changeName 함수가 새로운 객체를 반환하도록 수정
기존 정보를 복사해서 새로운 객체를 반환하는 함수(얕은 복사)
var copyObject = function(target) {
var result = {};
for (var prop in target) {
result[prop] = target[prop];
}
return result;
};
var user = {
name: 'Jaenam';
gender: 'male';
};
var user2 = copyObject(user);
user2.name = 'Jung';
if (user !== user2) {
console.log('유저 정보가 변경되었습니다.'); // 유저 정보가 변경되었습니다.
}
console.log(user.name, user2.name); // Jaenam Jung
console.log(user == user2); // false
- copyObject는 for in 문법을 이용해 result 객체에 target 객체의 프로퍼티들을 복사하는 함수
- 이제 user는 불변 객체
- 다만 copyObject함수를 사용할 때만 불변 객체이므로 immutable.js, baobab.js 등의 라이브러리를 사용하여 시스템적으로 제약을 거는 편이 더 안전함
- 얕은 복사만을 수행한다는 점에서도 한계 있음
얕은 복사와 깊은 복사
- 얕은 복사(shallow copy): 바로 아래 단계의 값만 복사하는 방법
- ex) 중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 주솟값만 복사
- 즉 원본과 사본이 동일한 참조형 데이터 주소를 가리키므로 사본을 바꾸면 원본도 변경됨
- 깊은 복사(deep copy): 내부의 모든 값을 하나하나 찾아서 전부 복사하는 방법
- ex) 중첩된 객체에서 프로퍼티 내부의 값들까지 전부 복사. 재귀로 구현 가능
깊은 복사
var copyObjectDeep = function(target) {
var result = {};
if (typeof target === 'object' && target !== null) {
for (var prop in target) {
result[prop] = copyObjectDeep(traget[prop]);
}
}else {
result = target;
}
return result;
}
- target이 객체인 경우에는 내부 프로퍼티를 순회하며 copyObjectDeep 함수를 재귀 호출, 객체가 아닌 경우 target을 그대로 지정
- 원본과 사본이 서로 완전히 다른 객체를 참조하게 하여 어느 쪽의 프로퍼티를 변경하더라도 다른 쪽에 영향을 주지 않는다.
6. undefined와 null의 비교
- undefined
- 값이 존재하지 않을 때 자바스크립트 엔진이 자동으로 부여
- 값을 대입하지 않은 변수, 즉 데이터 영역의 메모리 주소를 지정하지 않는 식별자에 접근할 때
- 빈 배열의 경우 undefined 아닌 [empty x 3(배열 크키)]로 나타남 -> 비어있어서 배열 메서드의 순회 대상에서 제외됨
- 객체 내부의 존재하지 않는 프로퍼티에 접근하려고 할 때
- return 문이 없거나 호출되지 않은 함수의 실행 결과
- 값을 대입하지 않은 변수, 즉 데이터 영역의 메모리 주소를 지정하지 않는 식별자에 접근할 때
- 값이 존재하지 않을 때 자바스크립트 엔진이 자동으로 부여
- null
- '비어 있음'을 명시적으로 나타내고 싶을 때 사용
- typeof null은 object라고 반환되는데, 이는 자바스크립트 자체 버그임. 따라서 변수의 값이 null인지 확인하려면 동등연산자(==) 대신 일치 연산자(===)로 확인해야함
'Javascript > 코어 자바스크립트' 카테고리의 다른 글
코어 자바스크립트: 3. this (0) | 2023.02.07 |
---|---|
코어 자바스크립트: 2. 실행 컨텍스트 (0) | 2022.12.23 |