[코어 자바스크립트] 1. 데이터타입(2)

송철진·2022년 9월 30일
0

1. 데이터 타입 배경지식

비트(bit): 0, 1로 표현한 하나의 메모리 조각. 고유식별자로 위치 확인.
바이트(byte): 8개의 비트를 묶은 메모리 조각 .
*바이트의 필요성: 비트 대비 값의 표현 개수 증가, 검색 시간 감소, 낭비되는 비트 최소화

정적타입 언어(C/C++, java)는 메모리 낭비 최소화 방안으로 데이터 타입별 메모리 영역을 2byte, 4byte 등으로 정해놓음

  • ex) short(정수형)는 2byte, int(정수형)는 4byte

Javascript는 메모리용량이 커진 후 나온 언어라서 메모리 압박으로부터 자유로움

  • ex) 숫자는 정수형/부동소수형 상관없이 8byte

모든 데이터는 바이트 단위의 식별자인 메모리 주솟값을 통해 서로 구분하고 연결함.

변수(variable): 변할 수 있는 수, 데이터
식별자(identifier): 변수명

2. 변수 선언과 데이터 할당

1. 변수 선언

var a;	// a는 식별자이며 변할 수 있는 데이터를 담는 공간, 그릇

변수영역

주소...100210031004
데이터이름: a
값:

2. 데이터 할당

var a;			//변수 a의 선언
a='abc';		//변수 a에 데이터 할당

var a = 'abc'	//변수 선언과 할당을 한 문장에 표현

왜 변수 영역에 값을 직접 대입하지 않을까?

  • 메모리의 효율적 관리를 위해서!
  • 문자열은 필요한 메모리용량과 글자수가 가변적이다
  • 직접 대입한다면? 변환된 데이터가 크다면 미리 확보된 공간에 대입하기 위해 공간 확장 작업이 필요함! 해당 공간 뒤에 저장된 데이터를 옮겨야 하고 이동시킨 주소를 식별자에 다시 연결해야 하고.. 연산량 증가!비효율!
  • 변수영역/데이터영역 나누면 중복된 데이터에 대한 처리효율 높아짐!

3. 기본형 데이터와 참조형 데이터

1. 불변값과 가변값(동작 방식)

변수(variable)변수영역 변경 가능(=재할당 가능)
상수(constant)변수영역 변경 불가(=재할당 불가)
불변값데이터영역 변경 불가
가변값데이터영역 변경 가능

  • 불변값: 기본형 데이터(숫자, 문자열, boolean, null, undefined, Symbol)
var a = 'abc';
a = a + 'def';	// @5003의 'abc'를 'abcdef'로 바꾸는 게 아니라
				// 데이터 영역에 'abcdef'를 새로 만들고 주소 저장

var b = 5;
var c = 5;		// 데이터 영역에서 5를 검색, 있으면 주소 재활용
b=7;			// @5005의 5를 7로 바꾸는게 아니라 
				// 데이터 영역에서 7을 검색, 없으면 새로 만들고 주소 저장

변수영역

주소...1002100310041005
데이터이름: a이름: b이름: c
값: @5003 @5004값: @5005 @5006값: @5005

데이터영역

주소...5002500350045005500650075008
데이터'abc''abcdef'57

  • 가변값: 참조형 데이터
  • 단, 참조형 데이터를 불변값으로 활용한 예외가 있음
  • 객체의 변수(프로퍼티) 영역이 별도로 존재한다
  • 데이터영역에 저장된 값은 모두 불변값이다. 그러나 변수에는 다른 값을 얼마든지 대입할 수 있다. 따라서 참조형 데이터는 불변하지 않다(가변값이다)

참조형 데이터의 할당과 프로퍼티 재할당:

var obj1 = {
  a: 1,
  b: 'bbb'
};
obj1.a = 2;	//변수영역(@1002)은 변하지 않음. 객체 내부의 값만 바뀜.

변수영역

주소10011002100310041005
데이터이름: obj1
값: @5001

데이터영역

주소5001500250035004500550065007
데이터이름: a1'bbb'2
값: @7003~?

객체 @5001의 변수영역

주소700370047005
데이터이름: a이름: b
값: @5003 @5005값: @5004

  • 중첩객체(nested object): 참조형 데이터의 프로퍼티에 다시 참조형 데이터를 할당
  • 참조 카운트: 어떤 데이터에 대해 자신의 주소를 참조하는 변수의 개수
  • 가비지 컬렉터(garbage collector, GC): (런타임 환경에 따라 특정 시점이나 메모리 사용량 포화 임박했을 때마다) 참조 카운트가 0인 메모리주소를 수거하여 새로운 값이 할당되도록 빈 공간으로 만드는 것.
var obj = {
  a: 3,
  arr: [3, 4, 5]	// 중첩 객체
};
obj.arr = 'str';

👉 GC 대상: 데이터영역(@5003), 객체 @5003의 변수영역(@8001~@8003)

변수영역

주소10011002100310041005
데이터이름: obj
값: @5001

데이터영역

주소5001500250035004500550065007
데이터이름: a3이름: arr45'str'
값: @7003~?값: @8001~?

객체 @5001의 변수영역

주소700370047005
데이터이름: a이름: arr
값: @5002값: @5003 @5006

객체 @5003의 변수영역

주소800180028003
데이터이름: 0이름: 1이름: 2
값: @5002값: @5004값: @5005

2. 변수 복사 비교

  • 변수를 복사했을 때 기본형과 참조형 데이터 모두 같은 주소(@5001, @5002)를 바라보게 되는 점에서 동일하다
  • 복사한 변수의 값을 변경했을 때 기본형 a,b는 서로 다른 주소(@5001, @5004)를 바라보게 됐고 참조형 obj1, obj2는 서로 같은 주소(@5002)를 바라본다는 점이 다르다.
a !== b
obj1 === obj2
  • '기본형은 값을 복사하고 참조형은 주솟값을 복사한다'(△)
    👉 엄밀히 따지면 자바스크립트의 모든 데이터 타입은 참조형 데이터이다.
    👉 기본형은 주솟값 복사 과정이 한 번만 있고 참조형은 한 단계 더 거친다.
  • 참조형데이터가 가변값인 경우는 내부의 프로퍼티를 변경할 때만 성립한다

객체 프로퍼티를 변경 시

var a = 10;
var b = a;			// 기본형 복사

var obj1 = {
  c: 10, 
  d: 'ddd'
};
var obj2 = obj1;	// 참조형 복사

b = 15;				//기본형) 복사한 변수의 값을 변경
obj2.c = 20;		//참조형) 복사한 변수의 값을 변경

변수영역

주소10011002100310041005
데이터이름: a이름: b이름: obj1이름: obj2
값: @5001값: @5001 @5004값: @5002값: @5002

데이터영역

주소50015002500350045005
데이터10@7103~?'ddd'1520

객체 @5002의 변수영역

주소71037104
데이터이름: c이름: d
값: @5001 @5005값: @5003

참조형 데이터 자체를 변경한 경우에는 불변값이다

객체 자체를 변경 시

var a = 10;
var b = a;			// 기본형 복사

var obj1 = {
  c: 10, 
  d: 'ddd'
};
var obj2 = obj1;	// 참조형 복사

b = 15;				//기본형) 복사한 변수의 값을 변경
obj2 = {
  c: 20,
  d: 'ddd'
};					//참조형) 복사한 변수의 값을 변경

변수영역

주소10011002100310041005
데이터이름: a이름: b이름: obj1이름: obj2
값: @5001값: @5001 @5004값: @5002값: @5002 @5006

데이터영역

주소500150025003500450055006
데이터10@7103~?'ddd'1520@8204~?

객체 @5002의 변수영역

주소71037104
데이터이름: c이름: d
값: @5001값: @5003

객체 @5006의 변수영역

주소82048205
데이터이름: c이름: d
값: @5005값: @5003

4. 불변 객체

1. 불변 객체를 만드는 법

객체의 불변성을 확보하려면 내부 프로퍼티를 변경할 필요가 있을 때마다 '매번 새로운 객체를 만들어 재할당'하거나, '자동으로 새로운 객체를 만드는 도구'를 활용하면 된다.

어떤 상황에서 불변 객체가 필요할까?
값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 경우

1) 기존 정보를 복사해 새로운 객체를 반환; 얕은 복사(shallow copy)

  • 중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 주솟값만 복사
  • 원본과 사본이 동일한 참조형 데이터 주소를 가리킴
  • 사본을 바꾸면 원본도 바뀌고, 원본을 바꾸면 사본도 바뀜
var copyObject = function(target){
  var result = {};
  for(var property in target){
    result[property] = target[property];
  }
  return result;
};

2) 깊은 복사(deep copy)

  • 내부의 모든 값들을 하나하나 찾아서 전부 복사

'target !== null' 조건을 덧붙인 이유는 자바스크립트 자체 버그로 typeof 명령어가 null에 대해서 'object'를 반환하기 때문이다

var copyObjectDeep = function(target){
  var result = {};
  if(typeof target === 'object' && target !== null){ 
    								// target이 객체인 경우
    for (var property in target){	// 내부 프로퍼티를 순회하며
      result[property] = copyObjectDeep(target[property]); 	
      								// 함수를 재귀호출
    }
  }else{
    result = target;				// 객체가 아니면 그대로 지정
  }
  return result;
}

그외 깊은 복사 방법

  • hasOwnProperty 메서드: 프로토타입 체이닝을 통해 상속된 프로퍼티를 복사하지 않게 함.
  • 객체를 JSON 문법으로 표현된 문자열로 전환 후 다시 JSON객체로 바꾸기.

3) 프로퍼티 변경을 할 수 없게 시스템적 제약을 거는 라이브러리: immutable.js, baobab.js 등

5. undefined와 null

undefined: 사용자가 명시적 지정(굳이?), 값이 존재하지 않을 때 JS엔진이 자동 부여

  • 값을 대입하지 않은 변수에 접근
  • 객체 내부의 존재하지 않는 프로퍼티에 접근
  • return문이 없거나 호출되지 않는 함수의 실행결과

null: 사용자가 '비어있음'을 명시적 지정

  • 어떤 변수의 값이 null인지 확인하려면 일치연산자(===)로 null과 비교!

배열도 객체이므로 값이 지정되지 않은 인덱스는 아직 존재하지 않는 프로퍼티 이다. 특정 인덱스에 값을 지정할 때 비로소 빈 공간을 확보하고 인덱스를 이름으로 지정하고 데이터의 주솟값을 저장하는 동작을 한다


var arr1 = [undefined, 1];
var arr2 = [];
arr2[1] = 1;

// 인덱스(i), 값(v), (p)
arr1.forEach(function(v,i){console.log(v,i);});
//undefined 0: 0번 인덱스의 값은 undefined입니다
//1 1: 1번 인덱스의 값은 1입니다
arr2.forEach(function(v,i){console.log(v,i);});
//1 1: (0번은 순회하지 않음) 1번 인덱스의 값은 1입니다 

arr1.map(function(v,i){return v+i);});
//[NaN, 2]: 0 + undefined = NaN, 1 + 1 = 2
arr2.map(function(v,i){return v+i);});
//[empty, 2] (비어있어서)empty, 1 + 1 = 2

arr1.filter(function(v){return !v);});
//[undefined]: ?
arr2.filter(function(v){return !v);});
//[]: ?

arr1.reduce(function(p,c,i){return p+c+i);});
//'undefined011': 이전반환값+현재처리할값+인덱스 => 'undefined'가 string이므로 이후의 덧셈 결과가 string으로 됨
arr2.reduce(function(p,c,i){return p+c+i);});
//'11': (0번 인덱스는 비어있어서 순회하지 않음) 왜 2가 아닌 '11'인가?

참조, 출처

[코어 자바스크립트], 정재남 저, 위키북스

profile
검색하고 기록하며 학습하는 백엔드 개발자

0개의 댓글