JavaScript

[코어 자바스크립트] 01. 데이터 타입 정리

개발하는 크롱 2021. 7. 30. 01:17
반응형

01. 데이터 타입

01) 데이터 타입의 종류

- 자바스크립트의 데이터 타입은 크게 두 가지: 기본형(원시형, primitive type), 참조형(reference type)

- 기본형: 숫자, 문자열, 불리언, null, undefined 등 + Symbol(ES6)

- 참조형: 객체(배열, 함수, 정규표현식, Map, WeakMap, Set, WeakSet 등이 객체의 하위 분류)

- 기본형은 불변성을 띔

 

02) 데이터 타입에 관한 배경지식

1. 메모리와 데이터

- C/C++, Java 등의 정적 타입 언어는 메모리 낭비 최소화를 위해 데이터 타입별로 할당할 메모리 영역을 2바이트, 4바이트 등으로 나누어 정해 놓음. 그 범위를 벗어나는 데이터를 입력하면 오류가 나거나 잘못된 값이 저장됨. 메모리 용량이 매우 부족했던 시절에는 불가피한 선택이었음.

- 자바스크립트는 메모리 용량이 과거보다 월등히 커진 상황에서 등장함. 따라서 메모리 공간을 좀 더 넉넉하게 할당함.

- 자바스크립트에서는 숫자를 정수형인지 부동소수형인지 구분하지 않고 8바이트를 확보함. 씨언어처럼 형변환을 걱정해야하는 상황이 훨씬 덜 발생.

- 각 비트는 고유한 식별자를 통해 위치를 확인할 수 있음

- 모든 데이터는 메모리 주솟값(바이트 단위의 식별자 == 시작 비트 식별자)을 통해 서로 구분 및 연결 가능

2. 식별자와 변수

- 변수: 변할 수 있는 수. 수라고 반드시 숫자라는 의미가 아님. 변할 수 있는 무언가(데이터: 숫자, 문자열, 객체 등)로 해석하는 게 적절.

- 식별자: 어떤 데이터를 식별하는 데 사용되는 이름. 즉, 변수명.

 

03) 변수 선언과 데이터 할당

1. 변수 선언

var a;

위 코드로 변수 선언 시 컴퓨터는 메모리에서 비어있는 공간 하나를 확보함. 그 다음 그 공간의 이름(식별자)을 a라고 지정함. 이후 사용자가 a에 접근하고자 하면 컴퓨터는 메모리에서 a라는 이름을 가진 주소를 검색해 해당 공간에 담긴 데이터를 반환할 것임.

 

2. 데이터 할당

- 변수 선언과 할당 과정

  1. 메모리에서 비어있는 공간 확보
  2. 확보한 공간의 식별자 지정
  3. 데이터 저장을 위한 별도의 메모리 공간을 확보해 데이터 저장
  4. 식별자 검색
  5. 데이터를 저장한 주소를 검색해 찾은 공간에 대입

 

- 처음 메모리에서 확보한 공간에 바로 값을 대입하지 않고 한 단계 더 거치는 이유?

  • 데이터 변환을 자유롭게 할 수 있게 함
  • 메모리의 효율적 관리
  • 문자열은 정해진 규격이나 문자수가 없음. 필요한 메모리 용량이 가변적임. 미리 확보한 영역 내에서만 데이터 변환이 가능하다면 처리해야 할 연산이 많아짐(확보된 공간을 변환된 데이터 크기에 맞게 늘리는 작업 필요하기 때문)

+) 문자열 변경 시 문자열을 새로 만들어 별도의 공간에 저장하고 그 주소를 식별자에 연결

 

04) 기본형 데이터와 참조형 데이터

1. 불변값

- 불변값과 상수는 다른 개념이다

- 상수와 변수는 "변수 영역 메모리의 변경 가능성"으로 구분함. 바꿀 수 있으면 변수, 없으면 상수임. 

- 위에서 "변수 영역 메모리의 변경 가능성"의 의미는 한 번 데이터 할당이 이루어진 변수 공간에 다른 데이터를 재할당할 수 있는지 여부

 

- "불변성 여부"를 구분할 때의 변경 가능성의 대상은 데이터 영역 메모리임.

- 기본형 데이터는 모두 불변값임. 참조형 데이터는 가변값인 경우가 많지만 100%는 아님(변경 불가 설정 혹은 불변값으로 활용하는 방법 있음)

 

- 변수에 불변값 할당할 때, 일단 데이터 영역에서 할당하려는 값이 있는지 찾고, 없으면 새로 만듦

- 불변값은 한 번 만든 값(데이터 영역) 변경 불가. 변경은 새로 만드는 동작을 통해서만 이뤄짐

- 기존 데이터는 자신의 주소를 저장하는 변수가 하나도 없게되면 가비지 컬렉터의 수거 대상이 됨

예) 5를 할당했던 변수 b의 값을 7로 바꾸는 과정

- 기존에 저장된 5 자체를 7로 바꾸는 것이 아님

- 기존에 저장했던(이미 만들어둔) 7이 있으면 그 주소를 재활용. 없으면 새로 만들어 b에 저장.

 

2. 가변값

- 참조형 데이터의 할당: 기본형 데이터와의 차이는 "객체의 변수(프로퍼티) 영역"이 별도로 존재함. 객체의 데이터 영역에는 프로퍼티 주소 목록 저장함.

- 참조형 데이터의 프로퍼티 재할당 시, 새로운 객체를 생성하는 것이 아니라(데이터 영역에 새 객체 만드는 것이 아님) 기존의 객체 내부 값을 변경하는 것임.

 

- 중첩 객체(nested object): 참조형 데이터의 프로퍼티에 다시 참조형 데이터를 할당한 경우

예)

var obj = {
    a: 3,
    arr: [1, 2, 3]
};

- 중첩 객체의 프로퍼티 할당 시 객체 내부의 객체도 참조형 데이터 할당 방식을 따른다

 

3. 변수 복사 비교

- 변수 복사 시 복사하려는 변수의 식별자를 검색해 해당 변수의 값을 찾아 대입

- 복사 과정은 기본형/참조형 데이터 모두 같은 주소를 바라보게 된다는 점에서 동일함

- 복사 과정은 동일하지만 데이터 할당 과정에서 이미 차이가 있기 때문에 변수 복사 이후의 동작에도 큰 차이 발생함

 

- 변수 복사 이후 값을 변경했을 때 차이점

 -> 기본형 데이터를 복사한 변수의 값을 변경하게 되면 해당 변수의 값이 달라짐. 즉 데이터 영역의 다른 주소를 가리키게 됨.

var a = 10;
var b = a; // 변수 복사

console.log(a); // 10
console.log(b); // 10

b = 20; // 기본형 데이터 복사한 변수의 값 변경

console.log(a); // 10
console.log(b); // 20

a랑 b는 이제 다른 값을 바라보게 됨. (a !== b)

 

-> 참조형 데이터를 복사한 변수의 프로퍼티 값 변경 시에는 해당 변수의 값이 그대로임. 여전히 같은 값을 가지고 있음. 

var obj1 = {c: 10, d: "str"};
var obj2 = obj1;

console.log(obj1.c); //10
console.log(obj2.c); //10

obj2.c = 20;

console.log(obj1.c); //20
console.log(obj2.c); //20

위의 코드에서 복사한 변수의 프로퍼티 값을 변경했지만 여전히 obj1과 obj2는 같은 값을 바라봄. (obj1 === obj2)

 

- 참조형 데이터를 복사한 변수의 프로퍼티 값이 아니라 객체 자체를 변경했을 때는 메모리의 데이터 영역에 새로운 공간에 새로운 객체가 저장되고 그 주소를 해당 변수의 변수 영역 위치에 저장. 즉 기존의 복사 대상과 값이 달라짐.

var obj1 = {c: 10, d: "abc"};
var obj2 = obj1;

console.log(obj1 === obj2); // true

obj2.c = 20;
console.log(obj1 === obj2); // true

obj2 = {c: 20, d: "abc"};
console.log(obj1 === obj2); // false

* 참조형 데이터가 "가변값"이라고 설명할 때의 "가변"은 참조형 데이터 자체를 변경하는 경우가 아니라 그 내부의 프로퍼티를 변경할 때만 성립. 데이터 자체를 변경하고자 하면(새로운 데이터를 할당하고자 하면) 기본형 데이터와 마찬가지로 기존 데이터 불변.

 

05) 불변 객체

1.  불변 객체를 만드는 간단한 방법

내부 프로퍼티가 아니라 데이터 자체를 변경하고자 하면 기본형 데이터와 마찬가지로 기존 데이터가 변하지 않는다. 따라서 내부 프로퍼티를 변경할 때마다 매번 새로운 객체를 만들어 재할당하기로 규칙을 정하거나 새로운 객체를 만드는 도구를 활용한다면 객체도 불변성을 확보할 수 있다.

- 불변 객체가 필요한 상황: 값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 상황

 

2. 얕은 복사와 깊은 복사

- 얕은 복사: 바로 아래 단계의 값만 복사하는 방법. 중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 "주솟값"만 복사. (원본 사본 모두 동일한 참조형 데이터의 주소를 가리키게 됨. 즉 원본 바뀌면 사본도, 사본 바뀌면 원본도 바뀜)

- 깊은 복사: 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법. 내부 프로퍼티가 객체면 재귀적으로 복사 함수를 호출하는 방식으로 하거나, JSON.parse(JSON.stringfy(target)으로 간편하게 가능. JSON 방식은 __proto__, getter/setter 등과 같이 JSON으로 변경할 수 없는 프로퍼티 무시.) 혹은 라이브러리 사용.

 

06) undefined와 null

자바스크립트에서 "없음"을 나타내는 두 가지 값.

'undefined'는 어떤 변수에 값이 존재하지 않음을 의미하고 'null'은 사용자가 명시적으로 '없음'을 표현하기 위해 대입한 값.

본래의 의미에 따라 사용자가 없음을 표현하기 위해 명시적으로 undefined 대입하는 것은 지양해야함.

 

[undefined]

- 사용자가 명시적으로 지정할 수도 있지만 값이 존재하지 않을 때 자바스크립트 엔진이 자동으로 부여하는 경우도 있음

- 자바스크립트 엔진이 사용자가 응당 어떤 값을 지정할 것이라고 예상되는 상황임에도 실제로는 그렇게 하지 않았을 때 undefined 반환

- 자바스크립트 엔진이 사용자가 응당 어떤 값을 지정할 것이라고 예상되는 상황

   1) 값을 대입하지 않은 변수, 즉 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근할 때(변수 선언시 자동으로 undefined 할당하는 것이 아니라 이후 해당 변수에 접근하고자 할 때 비로소 undefined를 반환하는 것)

   2) 객체 내부의 존재하지 않는 프로퍼티에 접근하려고 할 때

   3) return 문이 없거나 호출되지 않는 함수의 실행 결과

- '비어있는 요소'와 'undefined를 할당한 요소'는 출력 결과부터 다름. '비어있는 요소'는 순회와 관련된 많은 배열 메서드들의 순회대상에서 제외됨. undefined는 비록 비어있음을 의미하긴 하지만 하나의 값으로 동작하기 때문에 이때의 프로퍼티나 배열의 요소는 고유의 키값(프로퍼티 이름)이 실존하게 되고, 따라서 순회의 대상이 될 수 있음. 

- 값으로써 어딘가에 할당된 undefined는 실존하는 데이터인 반면, 자바스크립트 엔진이 반환해주는 undefined는 문자 그대로 값이 없음을 의미.

 

[null]

'비어있음'을 명시적으로 나타내고 싶을 때는 undefined가 아닌 null을 써라. 

위 규칙을 따르는 한 undefined가 "값을 대입하지 않은 변수에 접근하고자 할때 자바스크립트 엔진이 반환해주는 값"으로서만 존재 가능.

 

- 주의점: typeof null이 object임. (자바스크립트 자체 버그)

var n = null;
console.log(typeof n); // object

console.log(n == undefined); // true
console.log(n == null); // true

console.log(n === undefined); // false
console.log(n === null); // true
반응형