본문 바로가기

JavaScript/[책] 코어 자바스크립트 -ing

01. 데이터 타입 in JavaScript

(읽고 이해하는데 약 25분 소요됨)

 

01.  (자바스크립트)데이터 타입 종류

- 기본형(원시형, Primitive type): 값이 담긴 주소를 바로 복제하는 반면

- 참조형(Reference type): 주솟값들로 이뤄진 묶음을 가리키는 주솟값을 복제

 

 

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

- 비트: 0 또는 1만 표현할 수 있는 하나의 메모리 조각, 각 비트는 고유한 식별자를 통해 위치 확인 가능

- 메모리: 매우 많은 비트들로 구성

 

=> 비트가 가지고 있는 고유한 식별자를 통해 메모리 위치를 확인하는 것은 매우 비효율적(왜냐고? 비트 너무 많잖아!!)

=> 그렇다면 비트를 몇 개씩 묶어 하나의 단위로 여긴다면 표현할 수 있는 값도 많아지고, 메모리 주소 찾는 검색 시간도 줄겠지??

 

...암튼 그래서!! 나온게 뭐다?

 

- 바이트: 1바이트는 8개의 비트로 구성, 1비트마다 0 또는 1, 2가지 값 표현 가능, 그렇다면 1바이트는 256(2⁸)개의 값 표현 쌉가능!

바이트 역시, 시작하는 비트의 식별자로 위치 파악 가능

 

@나무위키

- 정적 타입 언어(C/C++, Java): (메모리의 낭비를 최소화하기 위해) 데이터 타입별로 메모리 영역 할당됨

예로 들면, 정수형타입(short)에는 2byte, short로 나타낼 수 없는 숫자를 표현하려면 4byte인 정수형타입(int)으로 형변환 해줘야 함

으... 넘나리 불편하다 불편해

 

- 동적 타입 언어(JavaScript): 메모리 용량이 과거보다 월등히 커진 상황에 만들어져서 이득 본 자바스크립트..

덕분에 메모리 공간을 좀 더 넉넉하게 할당됨. 숫자의 경우, 정수, 부동소수 구분하지 않고 64비트, 즉 8바이트!!!! 2⁶⁴ 값 표현 가능!!

 

모든 데이터는 바이트 단위의 식별자, 더 정확하게는 메모리 주솟값을 통해 서로 구분하고 연결할 수 있다.

 

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

- 변수: 변할 수 있는 데이터(숫자, 문자열, 객체, 배열 등등)

 

 

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

- 변수 선언:

var a;

1) 컴퓨터는 메모리에서 비어있는 공간 하나를 확보해 변할 수 있는 데이터를 만든다.

2) 이 데이터의 식별자(변수명)을 'a'로 한다.

주소 ... 1001 1002 1003 1004 ...
데이터     이름: a
값: 
     

 

- 사용자가 'a'에 접근하고자 할 때

1) 컴퓨터는 메모리에서 a라는 이름(식별자)을 가진 주소를 검색한다.

2) 그 주소에, 즉 해당 공간에 담긴 데이터(값)을 반환한다.

 

 

- 데이터 할당:

var a = "abc"; // 변수 선언과 할당을 한 문장으로 표현

1) 컴퓨터는 메모리에서 비어있는 공간 하나를 확보한다.

2) 확보한 공간의 이름, 즉 식별자(변수명)를 'a'로 한다.

3) 'a'라는 이름을 가진 주소를 검색해서 그곳에 문자열 'abc'를 할당한다????

실제로는 해당 위치에 문자열 'abc'를 직접 저장하지 않는다!!

3) 데이터를 저장하기 위한 별도의 메모리 공간을 다시 확보한다

4) 문자열 데이터 'abc'를 저장하고,

4) 'abc'가 저장된 그 주소를 holding 하고

5) 변수명이 'a'인 주소로 찾아가 홀딩된 주솟값을 값에 저장하는 식으로 이뤄진다.

주소 ... 1001 ① 1002 1003 1004 ...
데이터     이름: ② a
값: ⑤ @5001
     
주소 ... ③ 5001 5002 5003 5004 ...
데이터   ④ 'abc'        

 

👿 아니 근데 왜 'a'이름을 가진 공간에다가 바로 값을 대입하지 않고 굳이 번거롭게 한 단계 더 거치는 거야??? 👿

=> 데이터 변환을 자유롭게 할 수 있게 함과 동시에 메모리를 더욱 효율적으로 관리하기 위한 고민의 결과

=> 더 정확하게는 효율적으로 문자열 데이터의 변환을 처리하려면 변수와 데이터를 별도의 공간에 나누는 것이 최적

 

@펭수

읭??? 처음에 뭔 소리인가 했다.. 

 

내가 이해한 바로는...

 

자바스크립트 숫자형 데이터는 64비트(8바이트)의 공간을 확보하는데

그런데 문자열은 특별히 정해진 규격이 없고 한 글자마다 영어는 1바이트, 한글은 2바이트 등으로

각각 필요한 메모리 용량이 가변적이며, 전체 글자수 역시 가변적이다(얼마나 긴 문자열이 들어올지 모르잖아?!! 그러니깐 가변적인 거야)

 

그래서 만약 미리 확보된 공간 내에서만 데이터 변환을 할 수 있다면

변환한 데이터를 다시 저장하기 위해서는 '확보된 공간을 변환된 데이터 크기에 맞게 늘리는 작업'이 선행되어야 하는데 메모리 중간에 있는 데이터를 늘린다고 생각해봐.

 

해당 공간 뒤에 있는 데이터들 모두 전부 뒤로 옮겨야 되고, 주소 쓰인 곳에 가서 바뀐 주소로 싹 다 바꿔줘야 해! 으...

미리 확보된 공간에서 데이터를 직접 저장해야한다면 위와 같은 상황에서 컴퓨터가 처리해야할 연산은 겁나 많아질 수 밖에 없어!!!

 

나 일안해

=> 그래서 다시 말하자면?!! 문자열 데이터의 효율적인 변환을 위해서 변수와 데이터를 별도의 공간에 나누는 거야!

 

* 참고

값을 직접 저장 값의 주소를 저장  
데이터 할당 시 빠름(상대적) 데이터 할당 시 느림(상대적) 값을 직접 저장하는 경우와 비교했을 때

값의 주소를 저장하는 방식에서는
1. 먼저 동일한 데이터를 저장한 이력이 있는지를 전체 메모리에 걸쳐 검색을 하는 과정이 필요하고
2. (데이터가 있다면 그 값을 재활용하겠지만 없다면) 데이터를 저장할 공간을 확보한 다음 데이터를 넣고
3.  데이터가 담긴 주솟값을 다시 변수의 값으로 저장하는 단계가 

추가적으로 더 있기 때문에
상대적으로 데이터 할당이 더 느릴 수 밖에 없다.
비교에 비용이 많이 듦 비교에 비용이 안 듦 예시)
var a = 'abcdefasdkfd...';
var b = 'abcdefasdkfd...';
a === b 를 비교한다고 했을 때,

값을 직접 저장하는 경우:
문자열 하나하나를 다 비교해야만 한다. 문자열이 길면 길수록 비교 비용이 더 많아진다.

값의 주소를 저장하는 경우:
주솟값만 비교하면 되기 때문에 비용이 (거의) 들지 않는다.
메모리 낭비가 심함 메모리 낭비 최소화 예시)
메모리를 많이 차지하는 데이터가 여러 변수에 할당될 경우에

값을 직접 저장하게 되면:
예를 들어, 데이터가 100바이트일 경우에 메모리 크기는 (100 * 변수 개수)byte 가 된다.

값의 주소를 저장하는 경우:
100바이트가 넘는 데이터를 메모리 한 공간에 저장하고(같은 값이 전체 메모리 상에 오직 하나만 존재!) 그 주소를 변수의 값에 할당하면 되니깐
메모리 크기는 (100 + 변수 개수)byte 가 된다. 

 

 

 

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

- 불변값: 기본형 데이터(Prmitive type)인 Number, String, Boolean, null, undefined, Symbol 모두 불변값

(불변성 여부를 구분할 때의 변경 가능성의 대상은 데이터 영역 메모리이다. 즉, 데이터 영역에서 주소의 값이 바뀌지 않은 경우 '불변하다' 라고 한다.)

 

- 가변값:

참조형과 기본형 데이터와의 차이는 '객체의 변수(프로퍼티) 영역'이 별도로 존재한다는 점

'데이터 영역'은 기존의 메모리 공간은 그대로 활용한다.

 

- 중첩객체: 참조형 데이터의 프로퍼티에 다시 참조형 데이터를 할당하는 경우

- 참조카운트: 어떤 데이터가 담긴 메모리 주소를 참조하는 변수의 개수를 참조카운트라고 한다. 

참조 카운트가 0인 메모리 주소는 가비지 컬렉터(garbage collector, GC)의 수거 대상이 된다.

 

 

05. 불변 객체

- 값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 경우가 종종 발생하는데, 이럴 때 불변 객체가 필요하다.

 

참조형 데이터의 '가변'은 데이터 자체가(데이터 영역) 아닌 내부 프로퍼티를(객체의 변수영역에 있는 값) 변경할 때만 성립한다.

 

- 불변 객체를 만드는 간단한 방법:

var copyObject = function (target) {
  var result = {};
  for (var prop in target) {
    result[prop] = target[prop];
  }
  
  return result;
}

// copyObject를 이용한 객체 복사
var user = {
  name: 'Arcane',
  gender: 'male'
};

var user2 = copyObject(user);
user2.name = 'Chris';

if (user !== user2) {
  console.log('유저 정보가 변경되었습니다.');
}

console.log(user.name, user2.name); // Arcane Chris
console.log(user === user2); // false

 

- 얕은 복사: 바로 아래 단계의 값만 복사하는 방법(객체 프로퍼티 중 또 객체가 있으면 그 주솟값만 복사함ㅠㅠ)

var user = {
  name: 'Ellie',
  urls: {
    portfolio: 'https://github.com/abc',
    blog: 'https://blog.naver.com/abc'
  }
};

var user2 = copyObject(user);

user2.name = 'JJang';
console.log(user.name === user2.name); // false

user2.urls.portfolio = 'https://github.com/def';
console.log(user.urls.portfolio === user2.urls.portfolio); // true

user2.urls.blog = '';
console.log(user.urls.blog === user2.urls.blog); // true

 

- (중첩된 객체에 대한) 깊은 복사: 참조형 데이터인 프로퍼티도 불변 객체로 만드는 복사

var user2 = copyObject(user);
user2.urls = copyObject(user.urls);

// 객체의 깊은 복사를 수행하는 범용 함수
var copyObjectDeep = function(target) {
  var result = {};
  if (typeof target === 'object' && target !== null) {
    for (var prop in target) {
      result[prop] = copyObjectDeep(target[prop]);
    }
  } else {
    result = target;
  }
  
  return result
};


// JSON을 활용한 간단한 깊은 복사
var copyObjectViaJSON = function(target) {
  return JSON.parse(JSON.stringify(target));
};

 

※ 잠깐! 요약해보면

값을 복사하는 방법은 1) 얕은 복사(shallow copy) 2) 깊은 복사(deep copy) 2가지로 나뉜다.

깊은 복사는 쉽게 말하면 복사한 데이터의 값을 변경하더라고 원본데이터에 영향을 미치지 않는다.

얕은 복사는 복사한 데이터의 값을 변경하면 원본데이터에도 영향을 미친다.

기본형 데이터는 기본적으로 깊은 복사이고, 참조형 데이터는 얕은 복사이다.

스프레드 연산자를 사용하면 객체도 깊은 복사가 되는데 중첩객체일 경우 deep copy 못하기 때문에 중첩 객체까지도 깊은 복사를 해주는 처리를 해줘야 한다.

 

 

06. undefined 와 null

자바스크립트에는 '없음'을 나타내는 값이 2가지: undefined와 null

 

- undefined

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

1) 값을 대입하지 않는 변수, 즉 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근할 때

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

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

 

- null

'비어있음'을 명시적으로 나타내고 싶을 때는 undefined가 아닌 null을 쓰도록 하자!

 

 

1주차 스터디 회고

- 좋았던 점: 같은 책을 함께 읽고 개인적으로 중요하다고 생각되는 부분을 이야기 나눌 수 있어서 좋았다. 나와 다른 포인트를 들으면 그 부분을 한 번 더 체크하고 넘어갈 수 있었다. 또한 내가 중요하다고 느낀 부분을 다른 사람도 그렇게 느꼈을 때 그 부분을 다시 듣게되어 더 잘 이해하게 되었다. 처음 30~40분은 각 자 캠을 키고 책을 읽는 시간이었는데 처음 해보는 스터디 방식이었음에도 불구하고 엄청 집중하며 읽을 수 있었다. 스터디 방식이 나랑 꽤 잘 맞고 또 꽤나 많은 동기부여를 받아서 좋았다.

- 아쉬운 점: 프론트앤드 개발자가 되기 위해 공부하고 있는 입장이라 책을 읽고 내용을 이해하는 속도가 느렸다. 시간 내에 책을 다 읽지 못해서 내가 읽지 않은 부분에 관한 얘기를 나눌 때 적극 참여하지 못해서 좀 아쉬웠다. 여유가 된다면 미리 책을 조금 읽어 가야겠다.

그리고 만나는 시간을 8시로 착각해서 캠을 켜기 편한 공간에 있지 못했다. 그래서 초반에 핸드폰을 이리저리 옮기느라 스터디에 빨리 집중하지 못했다. 다음 스터디부터 빨리 퇴근하고 평안한 공간에 얼른 들어가야지.

 

 

추가로 공부하면 좋을 것들

- 내가 선언한 변수가 어떤 주소에 어떤 데이터로 저장되어 있는지 보고 싶었는데 자바스크립트는 로우레벨 메모리영역에 직접 접근할 수 있는 권한이 없음. 자바스크립트도 ABC 언어 중 하나여서 기본적인 구조는 비슷하기 때문에 아래 링크 참조해 보자(https://youngq.tistory.com/9)

- 메모리 크기 변화 확인할 수 있는 방법: 크롬 개발자도구 퍼포먼스탭에서 메모리 변화 트래킹 하는 방식(https://ui.toast.com/weekly-pick/ko_20210611)

- 자바스크립트 메모리 구조는 1) stack memory(변수, 기본형 데이터, 정적 할당) 2) heap memory(참조형 데이터, 동적 할당) 영역으로 나뉜다.

- 실제 메모리 주솟값은 16진수로 표기함(최종적으로는 컴퓨터가 이해하는 이진법으로 바꾸기는 하지만). 이 16진수로 표기된 주솟값은 1'바이트'의 공간이 확보됨.

- Temperal Dead Zone(TDZ)

https://ui.toast.com/weekly-pick/ko_20191014

'JavaScript > [책] 코어 자바스크립트 -ing' 카테고리의 다른 글

04. 콜백 함수 in JavaScript  (0) 2021.12.09
03. this in JavaScript  (0) 2021.12.02
02. 실행 컨텍스트 in JavaScript  (0) 2021.11.25