본문 바로가기
자바스크립트/코어 자바스크립트

코어자바스크립트 1장<데이터 타입>

by SONBAEJUN 2022. 8. 11.

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를 부여하는 경우

  1. 값을 대입하지 않은 변수, 즉 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근할 때
  2. 객체 내부의 존재하지 않는 프로퍼티에 접근하려 할때
  3. 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인지 판단하기 위해선 ===를 사용!)