
1. 데이터 타입 종류
1-1) 기본형
- 숫자(Number)
- 문자열
- boolean
- null
- undefined
- 심볼(ES6 추가)
1-2) 참조형
- 객체
- 배열
- 함수
- 날짜
- 정규표현식
- Map, WeakMap, Set, WeakSet(ES6 추가)
기본형과 참조형을 구분하는 기준
- 기본형은 값이 담긴 주솟값을 바로 복제
- 참조형은 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제
- 기본형은 불변셩 / 참조형은 예외가 있지만 대게 가변성
2. 데이터 타입에 관한 배경지식
2-1) byte
- JS는 숫자의 경우 구분없이 8byte를 확보, 문자열은 정해진 규격이 없다.
2-2) 변수와 식별자
- 변수와 식별자는 혼용되는 경우가 많지만 명확히 다르다
- 변수 = 변할 수 있는 수
- 식별자 = 어떤 데이터를 식별하는 데 사용하는 이름, 즉 변수명
3. 변수 선언과 데이터 할당
3-1)변수 선언
var a;
위 코드 실행 시 동작원리
ⓐ메모리에서 비어있는 공간 하나를 확보한다.
ⓑ이 공간의 이름(식별자)를 a라고 지정한다.
ⓒ이후에 사용자가 a에 접근하려고 하면 컴퓨터는 메모리에서 a라는 이름을 가진 주소를 검색하여 해당 공간에 담긴 데이 터를 반환할 것이다.
3-2) 데이터 할당
var a;
a = 'abc';
var a = 'abc';
※위 1,2번 줄과 4번째줄은 같은 원리로 동작하는 코드이다.
위 코드 실행 시 동작원리
※변수영역에 직접 대입하지않고 한 단계를 더 거치는 이유
==> 데이터 변환을 자유롭게 할 수 있게 함과 동시에 메모리를 더욱 효율적으로 관리하기 위해서
: JS는 숫자형 데이터에 대해 8바이트의 공간을 확보하지만 문자열은 정해진 규격이 없다.
그럼 데이터 변환을 할 때에 '확보된 공간을 변환된 데이터의 크기에 맞게 늘리는 작업'이 선행되야 한다.
그렇게 되면 컴퓨터의 연산이 늘어나고 비효율적으로 된다.
또 다른 경우로는 중복 할당시에 데이터 영역에 중복될 값을 지정하고 그 주솟값을 각 변수영역에 저장해주는 것이 훨씬 효율적이다.
3-3) 데이터 변환
var a = 'abc';
a = 'abcdef';
위 코드 실행 시 동작원리
변수 a를 재할당 시 변수 a가 가리키는 데이터 영역 값을 수정시키지 않고,
새로운 데이터 영역에 'abcdef'을 할당시킨 다음 변수 a의 값을 새로 할당한 데이터영역의 주솟값으로 변경한다
※이 때, @5004('abc')는 가비지 콜렉터가 일어날 때 수집 대상이 될 것이다.
4. 기본형 데이터와 참조형 데이터
4-1) 변수와 상수의 차이?
- 변수는 변경가능한 값, 상수는 변경 불가능한 값이다.
- 변수와 상수를 구분짓는 변경 가능성의 대상 = 변수 영역 메모리(한번 데이터 할당이 일어난 변수 영역에 새로운 데이터 재할당이 가능한지 여부)
- 불변성의 여부를 구분할 때의 변경 가능성의 대상 = 데이터 영역 메모리이다.
4-2) 불변값 이해를 돕는 예시
var a = 5;
var b = 5;
a = 7;
1)변수 a에 5를 할당하면
ⓐ데이터 영역에서 5를 찾고 없으면 새로운 데이터 공간에 5를 만들어 저장
ⓑ그 주솟값을 변수 a에 저장
2)변수 b에 5를 할당하면
ⓐ데이터 영역에서 5를 찾은 뒤 그 주솟값을 변수 b에 저장
3)변수 a를 7로 변경
ⓐ가리키고 있는 데이터 영역의 값을 7로 바꾸는게 아니라 새로운 데이터 영역에 7을 저장
ⓑ7을 담은 데이터 영역의 주솟값을 변수 a에 저장
==> 데이터 영역의 5와7은 변하지 않는 불변값이 된다. 변경은 새로 만드는 동작을 통해서만 이뤄진다.
(유일하게 불변값이 변하는 경우 = 가비지 콜렉팅)
4-2) 참조형 데이터의 할당
var obj1 = {
a: 1;
b: 'bbb'
};
ⓐ컴퓨터는 우선 변수 영역의 빈 공간(@1002)을 확보하고, 그 주소의 이름을 obj1로 지정
ⓑ여러개의 프로퍼티로 이뤄진 데이터 그룹이므로, 이 그룹의 내부 프로퍼티를 저장하기 위해 별도의 변수 영역을 마련하고, 그 영역의 주소(@7103~?)을 @5001에 저장
ⓒ @7103 및 @7104에 각각 a와 b라는 프로퍼티 이름을 저장한다
ⓓ 데이터 영역에 숫자1을 검색하고 없으므로 새로운 데이터 영역(@5003)에 저장 후 주솟값을 변수 a에 저장
마찬가지로 'bbb'역시 없으므로 새롭게 생성 후 그 주솟값을 @7104에 저장해준다.
※기본형 데이터와의 차이점은 '객체의 변수(프로퍼티) 영역' 이 별도로 존재한다는 점이다.
이 특성 때문에 객체의 변수공간은 언제든지 다른 공간을 할당가능하고, 따라서 가변하다.
4-2) 참조형 데이터의 프로퍼티 재할당
var obj1 = {
a: 1;
b: 'bbb'
};
obj1.a = 2;
ⓐobj1의 저장된 메모리값(@5001)은 변하지 않는다
ⓑobj1의 프로퍼티 a의(@7103)의 값을 2가 새로 만들어진 데이터 영역(@5005)로 변경시킨다.
4-2) 중첩된 참조형 데이터(객체)의 프로퍼티 할당
var obj = {
x: 3;
arr: [3,4,5]
};
obj객체의 지정(@1002)과 데이터 영역 @5001에서의 주소값 할당 및 데이터 3을 이용한 변수 x(@7103)의 주솟값 할당까지는 위에서 다룬 내용이다. 다만 @7104는 배열로서 역시 데이터 그룹이다. 따라서 이 그룹 내부의 프로퍼티들을 저장하기 위해서 다시 별도의 변수 영역을 마련해주고(@8104~?), 그 영역의 주소 정보(@8104~?)를 @5003에 저장 한 다음, @5003을 @7104에 저장한다.
그 이후, 배열의 요소가 총 3개이므로 3개의 변수공간을 확보하고, 각각의 인덱스를 부여한다(0,1,2)
그리고, 각각의 인덱스에 값을 대입시켜준다.
4-2) 중첩된 참조형 데이터(객체)의 프로퍼티 재할당
obj.arr = 'str';
ⓐ @5006에 'str'을 저장하고, 그 주소값을 @7104에 저장한다.
ⓑ 이렇게 되면 더 이상 자신의 주소를 참조하는 변수가 하나도 없게되는 @5003과 @8104~@8106은 참조카운트가 0이 된다. 참조카운트가 0인 메모리 주소는 가비지 컬렌터의 수거 대상이 된다.
4-2) 변수 복사 비교
var a = 10;
var b = a;
var obj1 = { c: 10, d: 'ddd' };
var obj2 = obj1
①기본형 데이터의 복사
ⓐ1번째 줄에서는 변수 영역의 빈 공간 @1001을 확보하고 식별자를 a로 지정
ⓑ숫자 10을 데이터 영역에서 검색하고 없으므로 빈 공간 @5001에 저장한 다음, 이 주솟값을 변수 a의 주소에 대입
ⓒ변수 영역의 빈 공간 @1002을 확보하고 식별자를 b로 지정
ⓓ식별자 a를 검색(@1001)하여 저장된 값인 @5001을 들고 좀 전에 확보해둔 @1002에 값을 대입
②참조형 데이터의 복사
ⓐ변수영역의 빈 공간 @1003을 확보하고 식별자를 obj1로 지정.
ⓑ데이터 영역의 빈 공간 @5002를 확보하고 데이터 그룹이 담겨야 하므로 별도의 변수 영역 @7103~?을 확보해 그 주소를 저장한다.
ⓒ@7103부터 @7104까지 식별자 c, d를 할당하고, 각각의 대입값을 검색 또는 생성 해 주솟값을 대입한다.
ⓓ변수 영역의 빈 공간 @1004를 확보하고 식별자를 obj2로 지정한다. 그 후 식별자 obj1을 검색(@1003) 해 그 값인 @5002를 들고, @1004에 값으로 대입한다.
4-2) 변수 복사 이후 값 변경 결과 비교
var a = 10;
var b = a;
var obj1 = { c: 10, d: 'ddd' };
var obj2 = obj1;
b = 15;
obj2.c = 20;
① b = 15 실행시
ⓐ데이터 영역에 15가 없으므로 @5004에 15를 저장 후 식별자가 b인 주소 @1002의 값을 @5004로 변경함
②obj2.c = 20 실행시
ⓐ데이터 영역에 20이 없으므로 @5005에 20을 저장 후 변수 영역에서 obj2(@1004)를 찾아 obj2의 값인 @5002가 가리키는 변수 영역에서 다시 c를 찾아 (@7103) 그곳에 @5005를 대입한다.
※기본형과 참조형의 가장 큰 차이점 ==> 복사값을 변경시 기본형은 a와 b가 서로 다른 주소를 바라보게 되지만
참조형은 obj1과 obj2가 여전히 같은 객체를 바라보고 있는 상태이다. 이를 코드로 표현하면
a !== b
obj1 === obj2
'기본형은 값을 복사하고 참조형은 주솟값을 복사한다'라고 대부분의 책에서 설명하지만 엄밀히 말하면
기본형도 결국 주솟값을 참조한다. 다만 기본형은 주솟값을 복사하는 과정이 한번, 참조형은 한 단계를 더 거치게 된다는 차이가 존재한다는 것이다.
5. 불변객체
- 불변객체는 최근의 React, Vue.js, Angular 등의 라이브러리나 프레임워크에서 뿐만 아니라 함수형 프로그래밍, 디자인 패턴 등에서도 매우 중요한 기초가 되는 개념이다
- 참조형 데이터의 '가변성'은 참조형 데이터 자체가 아니라 내부 프로퍼티를 변경할때만 성립하는 개념이다.
- 내부 프로퍼티를 변경할 필요가 있을 때마다 매번 새로운 객체를 만들어 재활당 하기로 규칙을 정하거나 자동으로 새로운 객체를 만드는 도구를 활용한다면 객체 역시 불변성을 확보할 수 있다
ex)객체의 가변성에 따른 문제점
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
user 객체를 생성 후, changeName함수를 통해 user 객체의 name 프로퍼티를 'Jung'으로 변경 후 그 리턴값을 user2에 담았다.
우리가 기대하는 결과는 if문의 조건이 성립하여 '유저 정보가 변경되었습니다.' 문구가 출력되는 것이지만
출력없이 if문을 통과한다. 콘솔을 찍어보면 두 변수의 name이 모두 'Jung'으로 출력되고 두 변수가 동일하다고 출력된다.
만약 위 코드의 if문 처럼 정보가 바뀐 시점에 알람을 보내야 한다거나, 바뀌기 전의 정보와 바뀐 후 의 정보 차이를 가시적으로 보여줘야 하는 등의 기능을 구현하려면 위와 같아선 안된다. 변경 전과 변경 후의 서로 다른 객체를 바라보게 해야된다 그렇게 고친 코드는 아래와 같다.
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 함수가 새로운 객체를 반환하도록 수정했다.
이제 user와 user2는 서로 다른 객체이므로 안전하게 변경 전과 후를 비교할 수 있다.
6. undefinde와 null
- 자바스크립트에서 '없음'을 나타내는 값은 undefined와 null. 두 값의 의미는 같은 거 같지만 미세하게 다르고, 사용하는 목적 또한 다르다
6-1) undefined
undefinde는 사용자가 명시적으로 지정할 수도 있지만 JS엔진이 자동으로 부여하는 경우도 있다.
자바스크립트 엔진이 자동으로 undefined를 부여하는 경우
- 값을 대입하지 않은 변수, 즉 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근할 때
- 객체 내부의 존재하지 않는 프로퍼티에 접근하려 할때
- return문이 없거나 호출되지 않는 함수의 실행 결과
var a;
console.log(a); // (1) undefined. 값을 대입하지 않은 변수에 접근
var obj = { a: 1 };
console.log(obj.a); // 1
console.log(obj.b); // (2) 존재하지 않는 프로퍼티에 접근
console.log(b); // c.f) ReferenceError: b is not defined
var func = function() {};
var c = func(); // (3) 반환(return) 값이 없으면 undefined를 반환한 것으로 간주.
console.log(c); // undefined
다만 [1] 값을 대입하지 않은 경우에 대해 배열의 경우는 undefined 조차 할당되지 않은 empty를 반환한다
그럼 비어있는 요소와 undefined는 뭐가 다르냐 ? ==> 비어있는 요소는 순회의 대상에서 제외된다.
※사용자가 명시적으로 부여한 undefined는 그 자체로 값이므로 고유의 키값(프로퍼티 이름)이 실존하고 순회의 대상이다
반면에 JS엔진에서 반환해주는 undefined는 해당 프로퍼티 내지 배열의 키값 자체가 존재하지 않음을 의미한다.
6-2) var과 let, const의 차이
- var 변수는 LE가 활성화 될 때 생성되면서 동시에 undefined 로 초기화 된다
- ES6에서 새로 등장한 let, const에 대해서는 undefined를 할당하지 않은 채로 초기화를 마치고 이후 특정 값을 할당하기 전까지는 해당 변수에 접근할 수 없다
- 따라서 혼란을 피하기 위해선 비어있음을 명시적으로 나타내주고 싶을땐 undefined가 아닌 null을 사용하는 것이 구분에 있어 더 편리할 것이다.
- (※다만 typeof null이 object라는 점은 주의사항. 따라서 어떤 값이 null인지 판단하기 위해선 ===를 사용!)
'자바스크립트 > 코어 자바스크립트' 카테고리의 다른 글
코어자바스크립트 6장<프로토타입> (0) | 2022.08.18 |
---|---|
코어자바스크립트 5장<클로저> (0) | 2022.08.17 |
코어자바스크립트 4장<콜백 함수> (0) | 2022.08.14 |
코어자바스크립트 3장<this> (0) | 2022.08.13 |
코어자바스크립트 2장<실행 컨텍스트> (0) | 2022.08.12 |