🌊 var, let, const
자바스크립트의 변수는 기본적으로 선언되고 나서 할당에 의해 초기화가 됨
var, let, const의 차이는 기본 특징, 스코프, 재할당 여부라는 키워드로 나눌 수 있음
기본 특징
- var
- 선언 후 초기값이 없을 경우 자동으로 undefined로 초기화
- 선언 라인 이전에 변수를 참조해도 에러 발생하지 않음
- let과 const
- 둘 다 선언 라인 이전에 변수를 참조할 수 없는 일시적 사각지대(TDZ) 구간을 가짐
- let
- 초기화 없이 선언만 하는 것이 가능하지만 var처럼 특정 값으로 자동 초기화되지는 않음
- const
- 반드시 선언과 동시에 초기화하지 않으면 에러 발생
스코프
- var
- 함수 레벨 스코프
- 중괄호가 함수일 때만 스코프로 간주
- let과 const
- 블록 레벨 스코프
- 함수뿐만 아니라 조건문이나 반복문의 중괄호 등도 스코프로 간주
- var보다 제약이 많아서 의도치 않게 변수가 바뀔 부작용이 더 적음
재할당 여부
- var와 let
- const (상수)
- 기본적으로 재할당 불가능
(상수도 값이 메모리에 저장되기 때문에 변수라고 할 수 있지만 상수는 '재할당이 금지'된 변수)
- 원시 값의 경우 재할당으로만 변경 가능해서 재할당이 불가능한 const에 할당된 원시 값은 어떠한 방법으로도 변경 불가
- 객체 값은 재할당이 아닌 주소 값에 연결된 실제 값을 직접 변경할 수 있어서 객체 값이 const에 할당되면 실제 값을 바꾸는 방법으로 변경 가능 (const에 저장된 주소 값을 바꾸는 것은 아니기 때문에 const임에도 값을 바꿀 수 있는 것처럼 보임)
권장되는 사용 방식
- 최대한 const를 사용 (재할당 불가, 의도치 않은 변수 사용에서의 부작용을 줄임)
- 가끔 재할당이 필요할 때만 let 사용
- var는 최대한 쓰지 않는다.
🥏 콜백 함수, Promise와 async/await
콜백 함수
- 함수의 인자로 들어가는 함수
- 이벤트, 타이머와 같은 비동기 로직을 처리하기 위해 사용할 수 있음
- 두세 단계만 중첩되어도 가독성이 급격히 떨어지고 로직 추가나 에러 처리가 어려워지는 콜백 지옥이 발생
Promise
- 콜백 지옥을 해결하고 비동기 작업을 동기 작업처럼 편하게 처리하기 위해 ES6에서 도입된 개념
- new Promise로 객체를 생성하여 resolve, reject 메서드를 자체적으로 받음
- Promise의 콜백 함수 내부의 비동기 로직이,
- 성공하면 resolve를 실행하고 then으로 이동,
- 실패하면 reject를 실행하고 catch로 이동
- then/catch에서는 이전 작업에서 전달 받은 반환 값을 가지고 이후의 로직을 실행
- 성공/실패 여부와 상관 없이 항상 실행되어야 할 로직은 finally
- 에러가 발생하면 가장 가까운 catch문으로 처리
async/await
- Promise를 더 가독성 있게 만든 syntactic sugar 문법
- 기존의 then/catch도 가독성이 떨어진다고 생각해서
- 단순히 async/await만 붙이고 try/catch문을 통해 더 쉽게 비동기 로직을 실행할 수 있도록 함
- 사용법
- 비동기 로직을 포함한 함수 앞에 async 키워드를 붙이고
- 함수 내부의 비동기 로직 앞에 await 키워드를 붙임
- 해당 비동기 로직이 완전히 실행되어 반환 값이 나온 후에야 다음 라인을 실행하게 됨
- 즉, 비동기 로직을 동기처럼 실행할 수 있음
🌊 스코프(Scope)
- 변수나 함수와 같은 식별자가 유효한 범위
- 자바스크립트 함수는 선언된 위치에 따라서 상위 스코프가 정적으로 결정됨
= 정적 스코프, 렉시컬 스코프
- 자바스크립트 변수는 앞에 붙는 키워드에 따라 스코프가 달라짐
- var: 함수 레벨 스코프
- let과 const: 블록 레벨 스코프
- 함수뿐만 아니라 조건문, 반복문 등의 모든 중괄호를 스코프로 간주
- 블록 레벨 스코프가 함수 레벨 스코프보다 제약이 더 많음
- 스코프는 중첩 가능 => 스코프 체이닝 발생
- 내부 함수에서 참조하고자 하는 변수를 바깥쪽 스코프로 한 단계씩 넓혀 나가면서 찾아나가는 것
- 찾고자 하는 값이 함수 스코프 내에 없다면 최종적으로 전역 스코프에서 찾게 됨
🥏 클로저(Closure)
- 개념
- 외부 함수가 종료되어 소멸된 후에도 내부 함수가 외부 함수의 지역 변수를 여전히 참조할 수 있는 현상
- 또는 그 내부 함수 자체
- 가능한 이유
- 자바스크립트 함수가 선언될 때 자신의 상위 스코프 환경을 기억하고 있기 때문 (정적 스코프)
- 장점
- 내부 함수에서 참조 가능한 스코프를 제한하여
- 참조할 값이 예측할 수 없는 곳에서 무방비하게 참조되는 걸 방지
🌊 호이스팅(Hoisting)
- 자바스크립트가 코드를 실행할 때 코드 평가 과정을 거치면서
- 선언문인 식별자들을 먼저 실행하는 과정에서
- 선언문이 선두로 끌어 올려져서 실행되는 것처럼 보이는 현상 (코드가 물리적으로 끌어올려지는 것은 아님)
- 변수 호이스팅과 함수 호이스팅으로 나뉨
변수 호이스팅
- var는 호이스팅 후에 초기화되어 있지 않을 경우 undefined로 자동 초기화되어 선언 라인 이전에 변수를 참조해도 에러가 발생하지 않음
- 그러나 이로 인해 변수가 어디에서나 참조 가능한 '전역 스코프'를 갖게 됨 => 의도치 않은 변수 재할당과 같은 문제점 발생
- 해결: ES6에서 let과 const 등장
- let은 선언 후에 호이스팅 되지만 var처럼 따로 초기화는 되지 않으며,
- const는 반드시 선언과 동시에 초기화를 해야만 에러가 발생하지 않음
- 둘 다 초기화 이전에는 변수를 참조할 수 없는 일시적 사각지대(TDZ) 구간을 가짐
함수 호이스팅
- function으로 선언된 함수 선언문에서 발생
- 런타임 이전에 함수명과 동일한 이름으로 식별자를 생성하고 함수 몸체를 할당 => 선언 라인 이전에 함수 호출 가능
- 함수 표현식(변수에 함수를 할당한 형태)은 변수 호이스팅을 따름
🥏 원시 타입과 참조 타입
- 자바스크립트의 7가지 데이터 타입
- string, number, boolean, null, undefined, Symbol, 객체
- 이 중에서 객체를 제외한 모든 타입은 원시 타입
- 원시 타입
- 값에 의한 전달 (pass by value)
- 초기화 이후의 값 변경은 새로운 메모리 공간을 할당하는 재할당으로만 가능
- 객체 타입
- 참조에 의한 전달 (pass by reference)
- 변수에 할당된 메모리에 실제 값이 아닌 객체의 ‘주소 값’이 저장됨
- 원시 값은 불변(immutable)하기 때문에 처음에 할당된 값을 직접 변경할 수 없지만, 주소 값만 저장하는 참조 값은 변경 가능
- 최근에는 코드 상의 부작용을 막기 위해 참조 값도 불변성을 유지하고자
- spread 문법을 (객체 depth만큼) 쓰거나 lodash의 deepClone 같은 방법으로 깊은 복사를 시도하고 있음
- 깊은 복사를 하면 원본 값과 복사 값 중 어느 하나를 수정해도 서로에게 영향을 미치지 않음
- 추가: map, filter와 같은 고차 함수를 통해 완전히 새로운 배열을 반환하게 할 수도 있음
🌊 화살표 함수(Arrow Function)
- 형태적 특징
- function 키워드 대신 두꺼운 화살표(‘⇒’)를 사용해서 선언
- return 문이 1줄일 때 함수 중괄호와 return문 생략 가능
- 객체를 반환할 때 return문 없이 소괄호로 감싸서 반환 가능
- this 특징
- 호출 시점에 this가 가리키는 대상이 결정되는 일반 함수의 동적 바인딩과 달리, 무조건 상위 스코프에 바인딩되는 정적 바인딩
- 익명 함수로만 가능
- 화살표 함수에 이름을 붙여 기명 함수로 쓰고 싶다면 변수에 함수를 할당하는 함수 표현식 형태로 사용
🥏 this에 대해
- 자바스크립트의 this는 동적으로 바인딩됨
- 어떤 환경에서 ‘호출’되느냐에 따라 this가 가리키는 것이 달라짐
- this 바인딩 4가지 방식
- 기본 바인딩: 단독 함수 안에 있는 this의 경우 전역 객체에 바인딩
- 암시적 바인딩: 객체 내부의 메서드 안에 있는 this는 해당 객체에 바인딩
- 명시적 바인딩: call, apply, bind 등의 메서드를 가지고 this가 가리키는 대상을 개발자가 명시적으로 지정
- new 바인딩: new 키워드를 붙여서 생성된 인스턴스 안에 있는 this의 경우 해당 인스턴스에 바인딩
🌊 call, apply, bind 메서드
- 셋 다 this를 명시적으로 바인딩하는 메서드
- bind는 this 바인딩 후에 실행은 해주지 않아서 실행을 하려면 뒤에 따로 소괄호를 붙여 함수 호출을 해줘야 함
- call과 apply는 this를 특정 값과 바인딩한 후에 실행까지 해 줌
- call과 apply 차이점: call은 인수를 하나씩 컴마로(’, ’) 넘기고 apply는 배열 형태로 한꺼번에 넘김
🥏 실행 컨텍스트
정의
- 자바스크립트 코드를 실행하는 데 필요한 환경 정보를 담고 있는 객체
동작 과정
- 전역 컨텍스트 생성
- 코드가 실행되면서 전역 실행 컨텍스트가 호출 스택에 가장 먼저 쌓임
- 전역 실행 컨텍스트: 전역 스코프에 선언된 변수와 함수를 정의
- 함수 컨텍스트 생성
- 함수가 호출되면 해당 함수의 실행 컨텍스트를 생성하여 호출 스택에 쌓음
- 함수 실행 컨텍스트: 함수의 지역 변수를 등록하는 등 함수 실행에 필요한 정보 저장
- 전체 코드 실행 및 컨텍스트 제거
- 콜 스택의 가장 맨 위에 있는 실행 컨텍스트부터 차례대로 호출하여 함수나 메서드를 실행하고 제거
- 맨 아래에 있는 전역 실행 컨텍스트까지 제거되면 전체 코드의 실행 종료
특징
1. 평가 단계
- 실행 컨텍스트가 생성되고 나서 현재 스코프에서 사용 가능한 식별자들을 등록하는 과정
- 이때 호이스팅 발생 (미리 등록된 선언문 식별자들이 코드 실행 시에 선두에 있는 것처럼 동작하는 것)
2. 실행 단계
- 코드 실행 중에 식별자들이 참조할 값을 실행 컨텍스트에서 찾아내어 코드 실행
- 코드 실행 과정에서 수정된 참조 값은 실행 컨텍스트에 다시 등록
🌊 동기와 비동기
- 동기 방식
- 한 작업이 완료된 후 다른 작업이 시작됨
- 순서는 보장할 수 있으나, 하나를 하는 동안 다른 걸 할 수 없는 블로킹 현상 발생
- 예: DB에서 데이터를 조회한 후 그 결과를 사용하여 다음 작업을 수행하는 경우 => 데이터 조회가 완료될 때까지 다음 작업 대기
- 비동기 방식
- 한 작업의 완료 여부와 상관 없이 다른 작업 진행
- 여러 작업을 동시에 진행할 수 있으나, 작업 순서는 보장할 수 없음
- 예: 서버로부터 데이터를 받아오는 동안 화면 UI의 다른 곳에서 사용자 입력을 받는 것 => 데이터를 받아오는 것보다 사용자 입력이 먼저 시작될 수 있음
🥏 ES6에 새롭게 도입된 문법
1. let, const 키워드
- var와의 차이: 블록 스코프 / 재선언 불가
- let은 재할당 가능, const는 재할당 불가
- 재선언, 재할당 모두 불가능한 상수 선언 키워드 const 추가
- 기존의 var보다 제한이 많아져서 의도치 않은 변수 재선언 및 재할당과 같은 부작용 방지
2. Promise
- 기존에 콜백 함수를 중첩하면 발생하는 콜백 지옥 현상 해결
- 비동기 로직을 마치 동기처럼 쉽게 작성할 수 있게 됨
- then/catch 형태의 후속 처리 메서드를 통해 로직 추가와 에러 처리를 효과적으로 할 수 있음
3. 화살표 함수
- 함수 정의 시 기존의 function 키워드 대신 두꺼운 화살표(‘⇒’)로 간략하게 선언
- this 사용이 수월해짐 (무조건 상위 스코프에 바인딩)
4. 객체/배열 구조 분해 할당
- 따로 변수에 담아서 꺼낼 필요 없이 내부 값들을 해체해서 꺼낸 요소들을 바로 사용 가능
5. 템플릿 리터럴
- 백틱(``) 문자열 안에 ${ }로 자바스크립트 변수를 넣어서 동적인 문자열을 가독성 있게 표현 가능