[출처/ 코어 자바스크립트]
01. 데이터 타입의 종류
02. 데이터 타입에 관한 배경지식
1-2-1 메모리와 데이터
컴퓨터는 모든 데이터를 0 또는 1로 바꿔 기억한다. 0 또는 1만 표현할 수 있는 하나의 메모리 조각을 비트bit라고 한다.
메모리는 매우 많은 비트들로 구성돼 있는데 각 비트는 고유한 식별자unique identifier를 통해 위치를 확인할 수 있다.
그런데 비트 단위로 위치를 확인하는 것은 매우 비효율 적이므로 몇 개씩 묶어 하나의 단위로 여긴다면 표현할 수 있는 값도 늘어나면서 검색시간도 줄일 수 있다.
하지만 이처럼 하면 동시에 낭비되는 비트가 생기기도 한다. 표현 가능한 개수에 어느 정도 제약이 따르더라도 크게 문제가 되지 않을 적정 공간을 묶는 편이 낫다는 생각으로 바이트byte 단위가 생겼다. 1바이트는 8개의 비트로 구성돼 있다.
한편 메모리 용량이 과거보다 월등히 커진 상황에서 자바스크립트는 메모리 공간을 좀 더 넉넉하게 할당했다.
숫자의 경우 64비트, 즉 8바이트를 확보한다. 이로 인해 개발자가 형변환을 걱정해야 하는 상황이 훨씬 줄었다.
각 비트는 고유한 식별자를 지니는데 모든 데이터는 바이트 단위의 식별자, 더 정확하게는 메모리 주솟값을 통해 서로 구분하고 연결할 수 있다.
1-2-2 식별자와 변수
변수는 '변할 수 있는 수'로 모든 데이터타입이 가능하다. 식별자는 어떤 데이터를 식별하는데 사용하는 이름으로 즉 변수명이다.
03. 변수 선언과 데이터 할당
1-3-1 변수 선언
var a;
위 예제를 풀어 쓰면 "변할 수 있는 데이터를 만들고 이 데이터의 식별자는 a로 한다."이다.
변수는 변경 가능한 데이터가 담길 수 있는 공간 또는 그릇이라고 생각하면 된다.
1-3-2 데이터 할당
var a; //변수 a 선언
a = 'abc' //변수 a에 데이터 할당
var a = 'abc' //변수 선언과 할당을 한 문장으로 표현
왜 변수 영역에 값을 직접 대입하지 않고 굳이 번거롭게 한 단계를 거칠까? 이는 데이터 변환을 자유롭게 할 수 있게 함과 동시에 메모리를 더욱 효율적으로 관리할 수 있기 때문이다.
자바스크립트는 숫자형 데이터에 대해 64비트(8바이트)의 공간을 확보한다고 했다. 반면 문자열은 특별히 정해진 규격이 없다. 한 글자마다 영어는 1바이트, 한글은 2바이트 등으로 각각 필요한 메모리 용량이 가변적이며 전체 글자 수 역시 가변적이기 때문이다.
만약 미리 확보한 공간 내에서만 데이터 변환을 할 수 있다면 변환한 데이터를 다시 저장하기 위해 확보된 공간을 다시 늘리는 작업이 선행돼야 한다. 이러다 보면 컴퓨터가 처리해야 할 연산이 많아지기 때문에 가장 효율적인 방법은 문자열 데이터의 변환을 처리하기 위해 변수와 데이터를 별도의 공간에 나누어 저장하는 것이다.
04. 기본형 데이터와 참조형 데이터
1-4-1 불변값
변수와 상수를 구분하는 성질은 '변경 가능성'이다. 바꿀 수 있으면 변수, 없다면 상수이다.
변수와 상수를 구분 짓는 변경 가능성의 대상은 변수 영역 메모리이다. 한번 데이터 할당이 이뤄진 변수 공간에 다른 데이터를 재할당할 수 있는지 여부가 관건이다. 반면 불변성 여부를 구분할 때의 변경 가능성의 대상은 데이터 영역 메모리다.
기본형 데이터인 숫자, 문자열, 불리언, null, undefined, Symbol은 모두 불변값이다.
var a = 'abc';
a = a + 'def';
var b = 5;
var c = 5;
b=7;
위 예제를 보면 변수 a에 문자열 'abc'를 할당했다가 뒤에 'def'를 추가하면 기존의 'abc'가 'abcdef'로 바뀌는 것이 아닌 새로운 문자열 'abcdef'를 만들어 그 주소를 변수 a에 저장한다. 'abc'와 'abcdef'는 완전히 별개의 데이터이다.
변수 b에 숫자 5를 할당한다. 그러면 컴퓨터는 일단 데이터 영역에서 5를 찾고, 없으면 데이터 공간을 하나 만들어 저장한다. 그 주소를 b에 저장한다. 변수 c를 다시 같은 수인 5를 할당하면 컴퓨터는 데이터 영역에서 5를 찾는다. 이미 만들어 놓은 값이 있으니 그 주소를 재활용하게 된다.
마지막 변수 b의 값을 7로 바꾸려고 하면 기존에 저장된 5 자체를 7로 바꾸는 것이 아니라 기존에 저장했던 7을 찾아서 있으면 재활용하고, 없으면 새로 만들어서 b에 저장한다. 결국 5와 7 모두 다른 값으로 변경할 수 없다.
이처럼 기존 값은 변경할 수 없고 변경하려면 새로 만드는 동작을 통해서만 이뤄줘야하는 것이 불변값의 성질이다.
1-4-2 가변값
참조형은 가변값인 경우가 많지만 설정에 따라 변경 불가한 경우도 있고, 아예 불변값으로 활용하는 방안도 있다.
var obj1 = {
a: 1,
b: 'bbb'
};
기본형 데이터와의 차이는 '객체의 변수(프로퍼티) 영역'이 별도로 존재한다는 점이다.
그림을 자세히 보면 객체가 별도로 할애한 영역은 변수 영역일 뿐 '데이터 영역'은 기존의 메모리 공간을 그대로 활용하고 있다. 데이터 영역에 저장된 값은 모두 불변값이다. 그러나 변수에는 다른 값을 얼마든지 대입할 수 있다. 바로 이 부분 때문에 참조형 데이터는 불변하지 않다(가변값이다)라고 하는 것이다.
var obj1 ={
a: 1,
b: 'bbb'
};
obj1.a = 2;
obj1의 a 프로퍼티에 숫자 2를 할당한다고 하면 데이터 영역에서 숫자 2를 검색한다. 결과가 없으므로 빈 공간인 @5005에 저장하고, 이 주소를 @7103에 저장한다. 변수 obj1이 바라보고 있는 주소는 @5001로 변하지 않는다. 즉 '새로운 객체'가 만들어진 것이 아니라 기존의 객체 내부의 값만 바뀐 것이다.
1-4-3 변수 복사 비교
var a = 10;
var b = a;
var obj = { c: 10, d: 'ddd' };
var obj2 = obj1;
이번에는 b의 경우와 마찬가지로 obj2에도 새로운 객체를 할당함으로써 값을 직접 변경했다. 그러면 메모리의 데이터 영역의 새로운 공간에 새 객체가 저장되고 그 주소를 변수 영역의 obj2 위치에 저장할 것이다. 객체에 대한 변경임에도 값이 달라진다.
즉, 참조형 데이터가 '가변값'이라고 설명할 때의 '가변'은 참조형 데이터 자체를 변경할 경우가 아니라 그 내부의 프로퍼티를 변경할 때만 성립한다.
05. 불변 객체
1-5-1 불변 객체를 만드는 간단한 방법
값으로 전달 받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 경우가 종종 발생한다. 이럴 때 불변 객체가 필요하다.
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
위 예제는 객체의 가변성으로 인한 문제점을 보여주는 예시이다. 1번째 줄에서 user 객체를 생성하고, 12번째 줄에서 user 객체의 name 프로퍼티를 'Jung'으로 바꾸는 함수 changeName을 호출해서 그 결과를 user2 변수에 담았다.
14번째 줄에서 user 변수와 user2 변수가 서로 같지 않다는 조건이 성립하면 15번째 줄의 내용이 출력되겠지만 실제로는 출력 없이 통과한다. 17번째 줄에서 두 변수의 name프로퍼티 모두 'Jung'으로 출력되고, 18번째 줄에서는 두 변수가 서로 동일하다고 한다.
만약 14번째 줄처럼 정보가 바뀐 시점에 알림을 보내야 한다거나, 바뀌기 전의 정보와 바뀐 후의 정보의 차이를 가시적으로 보여줘야 하는 등의 기능을 구현하려면 이대로 사용하면 안된다. 변경 전과 후에 서로 다른 객체를 바라보게 만들어야 한다. 고친 코드는 다음과 같다.
var copyObject = function (target) {
var result = {};
for (var prop in target) {
result[prop] = target[prop];
}
return result;
};
copyObject는 for in 문법을 이용해 result 객체에 target 객체의 프로퍼티들을 복사하는 함수이다. 몇 가지 아쉬운 점이 있긴 하지만 이전의 예제에서 user객체에 대해서는 문제가 되지 않으므로 일단 진행을 해보면
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 함수를 통해 간단하게 객체를 복사하고 내용을 수정하는데 성공했다. 이제부터 협업하는 모든 개발자들이 user 객체 내부의 변경이 필요할 때는 무조건 copyObject 함수를 사용하기로 합의하고 그 규칙을 지킨다는 전제하에서는 user객체가 곧 불변 객체라고 볼 수 있다.
1-5-2 얕은 복사와 깊은 복사
얕은 복사는 바로 아래 단계의 값만 복사하는 방법이고, 깊은 복사는 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법이다. 위 예제의 copyObject 함수는 얕은 복사만 수행했다. 이 말은 중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 주솟값만 복사한다는 의미이다. 그러면 해당 프로퍼티에 대해 원본과 사본이 모두 동일한 참조형 데이터의 주소를 가리키게 된다. 사본을 바꾸면 원본도 바뀌고, 원본을 바꾸면 사본도 바뀐다.
어떤 객체를 복사할 때 객체 내부의 모든 값을 복사해서 완전히 새로운 데이터를 만들고자 할 때, 객체의 프로퍼티 중에서 그 값이 기본형 데이터일 경우에는 그대로 복사하면 되지만 참조형 데이터는 다시 그 내부의 프로퍼티들을 복사해야 한다. 이 과정을 참조형 데이터가 있을 때마다 재귀적으로 수행해야만 비로소 깊은 복사가 된다.
06. undefined와 null
undefined와 null은 자바스크립트에는 '없음'을 나타내는 값이다.
undefined는 사용자가 명시적으로 지정할 수도 있지만 값이 존재하지 않을 때 자바스크립트 엔진이 자동으로 부여하는 경우도 있다. 자바스크립트 엔진은 사용자가 응당 어떤 값을 지정할 것이라고 예상되는 상황임에도 실제로는 그렇게 하지 않았을 때 undefined를 반환한다. 다음 세 가지의 경우가 이에 해당한다.
- 값을 대입하지 않은 변수, 즉 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근할 때
- 객체 내부의 존재하지 않는 프로퍼티에 접근하려고 할 때
- return 문이 없거나 호출되지 않는 함수의 실행 결과
null은 '비어있음'을 명시적으로 나타내고 싶을 때 쓰면된다. null은 주의할 점이 있는데 type of null이 object라는 점이다. 이는 자바스크립트 자체 버그이다. 따라서 어떤 변수의 값이 null인지 여부를 판별하기 위해서는 typeof 대신 다른 방식으로 접근해야 한다.
정리
- 자바스크립트 데이터 타입은 크게 기본형과 참조형이 있는데 기본적으로 기본형은 불변값이고 참조형은 가변값이다.
- 변수는 변경 가능한 데이터가 담길 수 있는 공간이고, 식별자는 그 변수의 이름을 말한다.
- 변수를 선언하면 컴퓨터는 우선 메모리의 빈 공간에 식별자를 저장하고, 그 공간에 자동으로 undefined를 할당한다. 이 후 그 변수에 기본형 데이터를 할당하려고 하면 별도의 공간에 데이터를 저장하고, 그 공간의 주소를 변수의 값 영역에 할당한다.
- 참조형 데이터를 할당하고자 할 경우 컴퓨터는 참조형 데이터 내부 프로퍼티들을 위한 변수 영역을 별도로 확보해서 확보된 주소를 변수에 연결하고, 다시 앞서 확보한 변수 영역에 각 프로퍼티의 식별자를 저장하고, 각 데이터를 별도의 공간에 저장해서 그 주소를 식별자들과 매칭시킨다. 이유는 참조형 데이터가 여러 개의 프로퍼티(변수)를 모은 '그룹'이기 때문이다. 이로 인해 참조형 데이터를 '가변값'으로 여겨야만 하는 상황이 발생한다. 참조형 데이터를 가변값으로 여겨야 하는 상황임에도 이를 불변값으로 사용하는 방법으로는 내부 프로퍼티들을 일일이 복사하면 된다(깊은 복사). 혹은 라이브러리를 사용할 수도 있다.
- '없음'을 나타내는 값은 두 가지가 있는데, undefined는 어떤 변수에 값이 존재하지 않을 경우이고 null은 사용자가 명시적으로 '없음'을 표현하기 위해 대입한 값이다.
'CoreJavaScript-study > dil' 카테고리의 다른 글
[DIL] 06 프로토타입 (4) | 2024.07.22 |
---|---|
[DIL] 05 클로저 (2) | 2024.07.15 |
[DIL] 04 콜백 함수 (1) | 2024.07.13 |
[DIL] 03 this (0) | 2024.07.10 |
[DIL] 02 실행 컨텍스트 (0) | 2024.07.04 |