모던 자바스크립트 Deep Dive (Ch.4 ~ Ch.7)

kozi·2023년 7월 13일
0

내용정리

목록 보기
3/7

4장 변수

4.1 변수란 무엇인가? 왜 필요한가?

10 + 20

자바스크립트로 위 코드를 실행하면 컴퓨터에서는 어떤 일이?
자바스크립트 엔진이 위 코드를 계산하려면 10, 20, + 라는 기호(리터럴literal과 연산자operator)를 알고 있어야 하며,
10 + 20 이라는 식(표현식expression)의 의미도 해석(파싱parsing)할 수 있어야 함.

컴퓨터는 cpu를 사용해 연산하고, 메모리를 사용해 데이터를 기억함.
메모리는 데이터를 저장할 수 있는 메모리 셀의 집합체
메모리 셀 하나의 크기는 1바이트이며, 컴퓨터는 이 단위로 데이터를 읽고 쓰고 한다.

컴퓨터는 모든 데이터를 2진수로 처리함.
위 식의 결과인 30 또한 메모리 상의 임의의 위치에 2진수로 저장됨.

이 때 연산 결과로 나온 30을 재사용하기 위해서는 저장된 메모리에 직접 접근해야하는데,
이러면 치명적 오류가 발생할 수 있음.
-> 따라서, 자바스크립트는 개발자의 직접적인 메모리 제어를 허용하지 않음.

허용해도 문제인게, 메모리 주소는 코드가 실행될 때 메모리 상황에 따라 임의로 결정됨.
-> 같은 코드를 실행해도 메모리 주소는 변경됨.

메모리 주소를 통해 값에 직접 접근하는 시도는 올바른 방법이 아님!

이것을 위해 변수가 있는 것.
변수의 정의:
하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름.
-> 프로그래밍 언어에서 값을 저장하고 참조하는 메커니즘으로, 값의 위치를 가리키는 상징적인 이름임.

변수는 하나의 값을 저장하기 위한 수단임.

// abc는 변수 이름, 1은 변수 값
var abc = 1;

객체나 배열 같은 자료구조를 사용하면, 여러 값을 하나로 그룹화해서 사용할 수 있음.

var abc = { id: 1, name: "kim" };

var abc = [
	{ id: 1, name: "Kim" },
    { id: 2, name: "Lee" }
];

4.2 식별자

식별자는 어떤 값을 구별해서 식별할 수 있는 고유한 이름임. (변수 이름을 식별자라고도 함.)

값은 메모리 공간에 저장돼있기 때문에 식별자는 어떤 값이 저장된 메모리 주소를 기억해야 함.

식별자는 값이 저장된 메모리 주소와 매핑 관계를 맺고, 이 매핑 정보도 메모리에 저장돼야 함.
-> 식별자는 값이 아닌 메모리 주소를 기억하고 있음!

변수, 함수, 클래스 등 메모리 상에 존재하는 어떤 값을 식별할 수 있는 이름은 모두 식별자라고 부름.

식별자는 네이밍 규칙 준수해야하고, 선언에 의해 자바스크립트 엔진에 존재를 알림.

4.3 변수 선언

변수 선언은 변수 생성을 말함.
-> 값 저장하기 위한 메모리 공간을 확보하고 변수 이름과 메모리 공간의 주소를 연결해서 값 저장할 수 있게 준비하는 것.

확보된 메모리는 해체되기 전까지는 누구도 사용할 수 없도록 보호되므로 안전하게 사용 가능함.

변수를 사용하려면 반드시 선언 필요.
var, let, const 키워드를 사용함.
ES6에서 let, const 도입되기 전까지 var이 유일한 키워드였음.

var score

변수 선언 후 메모리 공간에는 자바스크립트 엔진에 의해 undefined라는 값이 암묵적으로 할당되어 초기화됨. (자바스크립트의 독특한 특징)

자바스크립트 엔진은 변수 선언을 2단계에 거쳐 수행.
선언 단계: 변수 이름 등록, 엔진에 변수의 존재를 알림.
초기화 단계: 값 저장 위한 메모리 공간 확보, 암묵적으로 undefined 할당해 초기화.

  • 모든 식별자는 실행 컨텍스트에 등록됨. 실행 컨텍스트는 자바스크립트 엔진이 소스코드를 평가하고 실행하기 위해 필요한 환경을 제공하고 코드 실행 결과를 실제로 관리하는 영역임.
    변수 이름은 실행 컨텍스트 내에 키/값 형식인 객체로 등록되어 관리됨.

var 키워드를 통한 변수 선언은 선언, 초기화 단계가 동시에 진행됨.
(자바스크립트는 쓰레기 값이 나올 위험으로부터 안전함)

선언하지 않은 식별자에 접근하면 ReferenceError(참조 에러) 발생.

4.4 변수 선언의 실행 시점과 변수 호이스팅

console.log(score); // undefined

var score; // 변수 선언

변수 선언이 runtime이 아닌 이전 단계에서 먼저 실행되기 때문에 참조 에러 발생하지 않음.

자바스크립트 엔진은 순차적인 실행에 앞서 소스코드의 평가 과정을 거치고 소스코드 실행을 위한 준비를 함.
이 때, 모든 선언문을 찾아 먼저 실행함.
평가과정이 끝나면 모든 선언문을 제외하고 한 줄씩 순차적으로 실행함.
-> 어디서든지 변수 참조 가능

이렇듯 변수 선언문이 코드 선두로 끌어올려진 것처럼 동작하는 자바스크립트 고유의 특징을 변수 호이스팅이라고 함.

4.5 값의 할당

console.log(score); // undefined

var score;
score = 80;

console.log(score); // 80
console.log(score); // undefined

var score = 80;

console.log(score); // 80

선언과 할당을 단축 표현해도 자바스크립트 엔진은 선언과 할당을 2개의 문으로 나눠 각각 실행하기 때문에
위 두개는 동일하게 동작함.

console.log(score); // undefined

score = 80; // 값의 할당
var score; // 변수 선언

console.log(score); // 80

위처럼 해도 같은 결과가 나온다.(신기하다)

4.6 값의 재할당

엄밀하게는 처음에 할당하는 것도 재할당임. (암묵적 undefined 할당 -> 값 할당)
(const는 한 번만 할당할 수 있는 변수를 선언함.)

변수에 값 재할당하면, 원래 값이 있던 공간 지우는 게 아닌 새로운 메모리 공간 확보 후 그 공간에 새로운 값을 저장함.

변수의 이전 값처럼 불필요한 값들은 가비지 콜렉터에 의해 메모리에서 자동 해제됨. (단, 언제 해제될지는 예측 불가)

자바스크립트는 가비지 콜렉터를 내장하고 있는 매니지드 언어로, 이를 통해 메모리 누수를 방지함.
(매니지드, 언매니지드 언어는 일정한 생산성 확보(치명적 오류 제거)와 성능 간의 trade-off 존재함)

4.7 식별자 네이밍 규칙

식별자는 문자(특수문자 제외), 숫자, 언더스코어, 달러 기호 포함가능, 숫자로 시작하는 것은 허용하지 않음.
예약어는 식별자로 사용할 수 없음.

변수는 쉼표로 구분해 한번에 선언 가능하지만 가독성이 안좋아지므로 권장 X
한글, 일본어 식별자도 사용 가능하지만 권장 X
자바스크립트는 대소문자를 구별함.

네이밍 컨벤션
camelCase, snake_case, PascalCase, typeHungarianCase가 자주 사용됨

// typeHungarianCase
var strFirstName; // type + identifier
var $elem = document.getElementById('myId'); // DOM 노드
var observable$ = fromEvent(document, 'click'); // RxJS 옵저버블

일관성 유지한다면 어떤 네이밍 컨벤션도 좋지만, 일반적으로 변수나 함수 이름에는 카멜 케이스를,
생성자 함수, 클래스 이름에는 파스칼 케이스 사용함.
ECMAScript 사양에 정의된 객체와 함수들도 마찬가지이므로 가독성 높이기 위해서 카멜 케이스, 파스칼 케이스 따르는 것이 유리함.

5장 표현식과 문

5.1 값

값은 식(표현식)이 평가되어 생성된 결과를 말함.
평가란 식을 해석해서 값을 생성하거나 참조하는 것을 의미.

데이터 타입에 따라 다르게 해석됨.
메모리에 저장된 값 0100 0001을 숫자로 해석하면 65, 문자로 해석하면 'A'임

// 변수에는 10 + 20이 평가되어 생성된 값 30이 할당됨.
var sum = 10 + 20;

5.2 리터럴

리터럴은 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해 값을 생성하는 표기법을 말함.

예시:
2진수 리터럴: 0b01000001 (0b로 시작)
8진수 리터럴: 0o101 (0o로 시작)
16진수 리터럴: 0x41 (0x로 시작)
정규 표현식 리터럴 /[A-Z]+/g
... 등등

5.3 표현식

표현식은 값으로 평가될 수 있는 문(statement)이다. 표현식이 평가되면 새로운 값 생성하거나 기존값을 참조함.

리터럴은 값으로 평가됨. 따라서 리터럴 또한 표현식임.
값으로 평가될 수 있는 문은 모두 표현식.

표현식은 다른 표현식의 일부가 되어 새로운 값 만들 수 있음.

var x = 1 + 2;

// 식별자 표현식 x는 3으로 평가됨.
x + 3; // -> 6

5.4 문

문은 프로그램을 구성하는 기본 단위이자 최소 실행 단위임.
문은 여러 토큰으로 구성됨.
토큰이란? 문법적인 의미를 가지며, 문법적으로 더 이상 나눌 수 없는 코드의 기본 요소.

키워드, 식별자, 연산자, 리터럴, 세미콜론, 마침표 등 문법적 의미를 가지며, 문법적으로 더 이상 나눌 수 없는 코드의 기본 요소는 모두 토큰임.

문을 명령문이라고도 함.

선언문, 할당문, 조건문, 반복문 등으로 구분함.

5.5 세미콜론과 세미콜론 자동 삽입 기능

자바스크립트 엔진은 세미콜론으로 문이 종료한 위치를 파악하고 순차적으로 하나씩 문을 실행함.

but, 0개 이상의 문을 중괄호로 묶은 코드 블록, 예를 들어 if 문, for 문, 함수 등의 코드 블록은 자체 종결성을 갖기 때문에 세미콜론을 붙이지 않음.

문의 끝에 붙이는 세미콜론은 옵션(생략 가능)임.
자바스크립트 엔진이 소스코드 해석 시 세미콜론 자동 삽입 기능(ASI)이 암묵적으로 수행되기 때문.
하지만 간혹 ASI의 동작과 개발자 예측이 일치 않는 경우 있음.

function abc () {
  return
     {}
  // ASI의 동작 결과 -> return; {};
  // 개발자의 예측 -> return {};
}
console.log(abc()); // undefined

var def = function () {}
(function() {})();
// ASI의 동작 결과 -> var def = function () {}(function() {})();
// 개발자의 예측 -> var def = function () {}; (function() {})();
// TypeError: (intermediate value)(...) is not a function

5.6 표현식인 문과 표현식이 아닌 문

변수 선언문은 값으로 평가될 수 없으므로 표현식이 아님.

표현식인 문과 표현식 아닌 문을 구별하는 명료한 방법은 변수에 할당해 보는 것.

// 표현식 아닌 문은 값처럼 사용할 수 없음.
var abc = var x; // SyntaxError: Unexpected token var

변수 선언문은 값처럼 사용할 수 없음.

// 표현식인 문은 값처럼 사용할 수 있음.
var abc = x = 100;
console.log(abc); // 100

6장 데이터 타입

자바스크립트의 모든 값은 데이터 타입을 갖는다.
ES6는 7개 데이터 타입 제공함.
원시 타입: 숫자, 문자열, 불리언, undefined, null, 심벌
객체 타입: 객체, 함수, 배열 등

6.1 숫자 타입

자바스크립트는 하나의 숫자 타입만 존재함.

숫자 타입의 값은 배정밀도 64비트 부동소수점 형식을 따름.
즉, 모든 수를 실수로 처리, 정수를 위한 데이터 타입 별도 X

2진수, 8진수, 16진수 표현하기 위한 데이터 타입 제공 X
-> 값 참조하면 모두 10진수로 해석됨.

정수로 표시되는 수끼리 나눠도 실수가 나올 수 있음.

console.log(3 / 2); // 1.5

세 가지 특별한 값도 표현 가능
Infinity: 양의 무한대
-Infinity: 음의 무한대
NaN: 산술 연산 불가(not-a-number)

6.2 문자열 타입

텍스트 데이터 나타내는 데 사용함.
작은 따옴표, 큰따옴표, 백틱으로 텍스트를 감쌈
일반적인 표기법은 작은따옴표.

문자열을 따옴표로 감싸지 않으면 키워드나 식별자 같은 토큰으로 인식함.

자바스크립트의 문자열은 원시 타입이며, 변경 불가능한 값이다.

6.3 템플릿 리터럴

ES6부터 템플릿 리터럴이라고 하는 새로운 문자열 표기법이 도입됨.

6.3.1 멀티라인 문자열

일반 문자열 내에서 줄바꿈 등의 공백을 표현하려면 이스케이프 시퀀스 사용해야 함.(\n 등)

템플릿 리터럴 내에서는 줄바꿈이 허용되며, 모든 공백도 있는 그대로 적용됨.

var template = `<ul>
  <li></li>
</ul>`;

// 출력 결과

<ul>
  <li></li>
</ul>

6.3.2 표현식 삽입

문자열은 문자열 연산자 +를 사용해 연결 가능함.

템플릿 리터럴 내에서는 표현식 삽입을 통해 간단히 문자열 삽입 가능.

var a = 'hi'
var b = 'hello'

console.log(`Good ${a} ${b}.`) // Good hi hello.

표현식의 평가 결과가 문자열이 아니어도 문자열로 타입이 강제 변환되어 삽입됨.

6.4 불리언 타입

참, 거짓을 나타내는 true, false

6.5 undefined 타입

변수 참조 시 undefined 반환되면 초기화되지 않은 변수라는 것 간파 가능.

6.6 null 타입

변수에 값이 없다는 것을 의도적으로 명시할 때 사용함.

함수가 유효한 값을 반환할 수 없는 경우 명시적으로 null을 반환하기도 함.
document.querySelector 메서드는 조건 부합하는 HTML 요소 검색할 수 없는 경우 에러 대신 null을 반환함.

6.7 심벌 타입

변경 불가능한 원시 타입의 값. 다른 값과 중복되지 않는 유일무이한 값임.
충돌할 위험이 없는 객체의 유일한 프로퍼티 키를 만들기 위해 사용함.
Symbol 함수를 호출해 생성.
생성된 심벌 값은 외부에 노출되지 않고, 다른 값과 절대 중복되지 않음.

var key = Symbol('key')
console.log(typeof key); // symbol

// 객체 생성
var obj = {};

// 충돌 위험 없는 심벌을 프로퍼티 키로 사용
obj[key] = 'value'
console.log(obj[key]); // value

6.8 객체 타입

11장에서 자세히 살펴봄.

중요한 것은 자바스크립트를 이루고 있는 거의 모든 것이 객체라는 것.

6.9 데이터 타입의 필요성

6.9.1 데이터 타입에 의한 메모리 공간의 확보와 참조

값을 저장할 (확보해야 할) 메모리 공간의 크기를 결정해야함

숫자 타입은 8바이트 단위로 읽지 않으면 값이 훼손됨.

6.9.2 데이터 타입에 의한 값의 해석

메모리에서 읽어 들인 2진수를 어떻게 해석해야 하느냐가 문제임.

데이터 타입이 필요한 이유는 다음과 같음.

  • 값을 저장할 때 확보해야하는 메모리 공간의 크기를 결정하기 위해
  • 값을 참조할 때 한번에 읽어 들여야 할 메모리 공간의 크기를 결정하기 위해
  • 메모리에서 읽어들인 2진수를 어떻게 해석할지 결정하기 위해

6.10 동적 타이핑

6.10.1 동적 타입 언어와 정적 타입 언어

정적 타입 언어는 컴파일 시점에 타입 체크 수행함.

자바스크립트의 변수는 선언이 아닌 할당에 의해 타입이 결정(타입 추론)됨.
재할당에 의해 변수의 타입은 언제든지 동적으로 변할 수 있음.

이러한 특징을 동적 타이핑이라 함.

6.10.2 동적 타입 언어와 변수

복잡한 프로그램에서는 변화하는 변수 값 추적하기 어려울 수 있음.

또한 엔진에 의해 암묵적으로 타입이 자동으로 변환되기도 함.

변수 사용 주의사항

  • 꼭 필요한 경우에 한해 제한적으로 사용
  • 유효 범위는 최대한 좁게
  • 전역 변수는 최대한 자제
  • 변수보다는 상수를 사용
  • 목적이나 의미 파악 가능하게 네이밍

가독성이 좋은 코드가 좋은 코드다.

7장 연산자

7.1 산술 연산자

피연산자 대상으로 수학적 계산 수행, 새로운 숫자 값 만듦.
산술 연산이 불가능한 경우, NaN을 반환함.

7.1.1 이항 산술 연산자

2개의 피연산자를 산술 연산하여 숫자 값을 만듦.
피연산자의 값이 바뀌는 경우는 없고, 새로운 값을 만듦.

7.1.2 단항 산술 연산자

증가/감소(++, --) 연산자는 부수 효과(피연산자 값 변경) 있음.

전위, 후위는 다른 연산을 먼저 수행하는지의 순서를 결정함.

+는 숫자 타입 변환.
-는 부호 반전

7.1.3 문자열 연결 연산자

피연산자 중 하나 이상이 문자열인 경우 문자열 연결 연산자로 동작함.

암묵적으로 타입이 자동 변환되기도 함.

1 + true 연산하면 암묵적으로 true를 숫자 타입으로 강제 변환 후 연산 수행함.
이를 암묵적 타입 변환 또는 타입 강제 변환이라고 함.

+/- 단항 연산자도 암묵적 타입 변환이 발생한 것.

7.2 할당 연산자

우항에 있는 피연산자의 평가 결과를 좌항에 있는 변수에 할당함.
변수 값이 변하는 부수 효과가 있음.

할당문은 표현식인 문이다.

var x;

console.log(x = 10); // 10

연쇄 할당

a = b = c = 0;
console.log(a, b, c); // 0 0 0

7.3 비교 연산자

좌항, 우항 피연산자 비교한 다음 그 결과를 불리언 값으로 반환.

7.3.1 동등/일치 비교 연산자

동등 비교 연산자와 일치 비교 연산자는 비교의 엄격성 정도가 다름.

동등은 느슨, 일치는 엄격(타입까지)

동등 비교 연산자는 비교할 때 먼저 암묵적 타입 변환을 통해 타입을 일치시킨 후 같은 값인지 비교함.
타입 변환 후 같은 값일 수 있다면 true를 반환함.
따라서 동등 비교 연산자는 사용하지 않는 편이 좋다.

일치 비교 연산자에서 주의할 것은 NaN임.
NaN은 자신과 일치하지 않는 유일한 값임.

숫자가 NaN인지 조사하려면 빌트인 함수 Number.isNaN을 사용함.

숫자 0 또한 양의 0 음의 0을 비교하면 true를 반환함. (일치/동등 모두)

7.3.2 대소 관계 비교 연산자

피연산자 크기 비교하여 불리언 값 반환함.

7.4 삼항 조건 연산자

첫번째 피연산자가 true로 평가되면 두 번째,
false로 평가되면 세 번째 피연산자를 반환함.

if ... else 문은 표현식이 아닌 문이므로 값처럼 사용할 수 없다.
삼항 조건 연산자 표현식은 값처럼 사용할 수 있다.
(다른 표현식의 일부가 될 수 있음)

var x = 10;

var result = x % 2 ? '홀수' : '짝수';
console.log(result); // 짝수

7.5 논리 연산자

우항과 자항의 피연산자를 논리 연산함.
|| 논리합
&& 논리곱
! 부정

논리 부정 연산자는 언제나 불리언 값 반환.
단, 피연산자가 반드시 불리언 값일 필요는 없음.
불리언 값이 아닌 경우 불리언 타입으로 암묵적 타입 변환됨.
!0; -> true
!"Hello"; -> false

논리합, 논리곱 연산자 표현식의 평가 결과는 불리언 값이 아닐 수도 있음.
언제나 2개의 피연산자 중 어느 한쪽으로 평가됨.

'Cat' && 'Dog'; // -> 'Dog'

9.4절 단축 평가에서 자세히 살펴봄.

7.6 쉼표 연산자

왼쪽 피연산자부터 차례대로 피연산자를 평가.
마지막 피연산자 평가 끝나면 마지막 피연산자 평가 결과 반환함.

var x, y, z;

x = 1, y = 2, z = 3; // 3

7.7 그룹 연산자

그룹 연산자는 연산자 우선순위가 가장 높다.
10 * (2 + 3) -> 50

7.8 typeof 연산자

피연산자의 데이터 타입을 문자열로 반환함.
"string", "number", "boolean", "undefined", "symbol", "object", function"
null 값은 "object"를 반환함.
자바스크립트 첫 번째 버전의 버그임.
기존 코드에 영향을 줄 수 있기 때문에 아직까지 수정되지 못하고 있음.
null 타입인지 확인할 때는 일치 연산자를 사용하는 게 좋음.

선언하지 않은 식별자를 typeof 할 때 주의. (undefined 반환하므로)

7.9 지수 연산자

좌항의 피연산자를 밑으로, 우항의 피연산자를 지수로 거듭 제곱하여 숫자 값 반환함.
2 ** -2 // -> 0.25

음수를 거듭제곱의 밑으로 사용하려면 괄호로 묶어야 함.
(-5) ** 2

7.10 그 외의 연산자

해당 주제 소개하는 장에서 살펴보기.

7.11 연산자의 부수 효과

부수 효과가 있는 연산자는 할당 연산자(=), 증가/감소 연산자(++/--), delete 연산자다.

7.12 연산자 우선순위

기억하기 어렵고, 실수하기 쉽다.
-> 연산자 우선순위가 가장 높은 그룹 연산자를 사용하여 우선순위를 명시적으로 조절하자.

7.13 연산자 결합 순서

연산자의 어느 쪽(좌, 우)부터 평가 수행할 것인지 나타내는 순서를 말함.
결합 순서는 다음과 같음.

좌항 -> 우항: +, -, /, %, <, <=, >, >=, &&, ||, ., [], (), ??, ?., in, instanceof
우항 -> 좌항: ++, --, 할당 연산자(=, +=, -=, ...), !x, +x, -x, ++x, --x, typeof, delete, ? ... : ..., **

profile
어제보다 잘하고 싶은 개발자 Kozi 입니다.

0개의 댓글