호이스팅과 TDZ를 알기 전 먼저 스코프에 대해 알아야 한다.😃
자바스크립트는 코트의 영역을 스코프로 나누어 관리한다.
스코프는 단방향으로 연결되는 체인을 형성하며, 이 스코프 체인을 통해 상위 스코프로 이동하면서 식별자를 검색한다.
클로저와 밀접한 관계를 갖기 때문에 꼭 알아두어야하는 개념
모든 식별자는 자신이 선언된 위치에 의하여 다른 코드가 식별자 자신을 참조할 수 있는 유효 범위가 결정된다.
한 스코프 내에서는 식별자가 유일해야 하지만, 다른 스코프에는 동명의 식별자를 사용할 수 있음.
자바스크립트 엔진은 스코프를 통해서 어떤 변수를 참조할 것인지 결정함.
전역은 코드의 가장 바깥 영역으로, 여기서 선언된 변수는 전역 변수가 됨.
전역 변수는 어디서든 참조 가능
지역은 함수 코드의 내부 영역으로, 여기서 선언된 변수는 지역 변수가 됨.
지역 변수는 자신의 지역 스코프 및 하위 지역 스코프에서 참조 가능
지역 스코프는 함수에 의해서 생성되는가, 코드 블록에 의해서 생성되는가에 따라 레벨이 나뉨.
if
, for
, while
, try/catch
등 코드 블록이 지역 스코프 생성
let
, const
키워드로 선언된 변수는 모든 코드 블록을 지역 스코프로 인정함.
함수가 지역 스코프 생성
var
키워드로 선언된 변수는 오직 '함수'만을 지역 스코프로 인정함.
자바스크립트에서는 함수 정의가 평가되는 시점에 상위 스코프가 정적으로 결정됨.
즉, 함수를 어디서 호출했는지가 아니라 '어디에 정의했는지'에 따라서 상위 스코프가 결정됨.
함수의 상위 스코프는 함수 정의가 실행될 때 정적으로 결정되며, 함수 정의가 실행되어 생성된 함수 객체는 상위 스코프를 기억함.
⇒ 클로저와 깊은 연관
- 클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말한다.
- 간단히 말하면 클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수다
스코프는 함수의 중첩에 의해 계층적 구조를 가진다.
변수를 참조할 때, 자바스크립트 엔진은 스코프 체인을 통해
변수를 참조하는 코드의 스코프 =>>> 상위 스코프로 이동
선언된 변수를 검색한다.
스코프 체인은 outerEnvironmentReference
와 밀접한 관계를 가짐.
일시적으로 죽은 공간이라는 뜻
무슨 의미일까?? 🤔
let a = 1;
{
console.log(foo);
let a = 2;
}
간단히 여기서 중괄호
안의 구역을 TDZ라고 한다
var
은 TDZ의 영향을 안받는다let
, const
는 TDZ의 영향을 받고 이 떄문에 Reference Error
가 출력되는 것이다.호이스팅은 코드를 실행하기 전 변수선언/함수선언을 해당 스코프의 최상단으로 끌어올리는 것이 아니다.
호이스팅은 코드가 실행하기 전 변수선언/함수선언이 해당 스코프의 최상단으로 끌어 올려진 것 같은 현상을 말한다.
자바스크립트 엔진은 코드를 실행하기 전 실행 가능한 코드를 형상화하고 구분하는 과정( 실행 컨텍스트를 위한 과정 )을 거친다.
자바스크립트 엔진은 코드를 실행하기 전 실행 컨텍스트를 위한과정에서 모든 선언(var, let, const, function, class)을 스코프에 등록한다.
실행 컨텍스트는 실행 가능한 코드가 실행되기 위해 필요한 환경을 의미하고 실행되기전 이러한 실행 컨텍스트 과정(코드를 구분하는 과정)을 거친다.
자바스크립트의 모든 선언에는 호이스팅이 일어난다.
하지만 let
, const
, class
를 이용한 선언문을 호이스팅이 발생하지 않는 것처럼 동작한다.
이는 var
, let
, const
의 호이스팅 단계가 다르기 때문이다( ̄︶ ̄)↗
var
의 호이스팅 단계📕📗 => 📘let
의 호이스팅 단계📕 => 📗 => 📘const
의 호이스팅 단계📕📗📘var a = 1;
{
console.log(a);
var a = 2;
}
// 1출력
var
는 호이스팅 시 선언 && 초기화가 동시에 발생하여 출력 이후 지정된 변수는 undefined로 초기화 되고 전역변수로 선언된 1이 출력된다
let a = 1;
{
console.log(a);
let a = 2; //TDZ에 갇힘
}
// Reference Error(참조 에러)
let
은 호이스팅 시 선언이 이루어진 후, 할당라인에서 초기화 => 할당으로 진행되기 때문에 선언은 됐지만 참조가 되지 않아 Referenc Error
가 출력된다
const a = 1;
{
console.log(a);
const a = 2; //TDZ에 갇힘
}
// Reference Error(참조 에러)
const
는 호이스팅 시 선언 && 초기화 && 할당이 동시에 이루어져야 하는데 선언만 호이스팅되어 Reference Erorr
가 출력된다.
또한 const
는 재할당이 되지 않는다
자바스크립트에서 함수를 선언하는 방법은 2가지가 있다.
var add = function (x, y) {
return x + y;
}
function add(x, y) {
return x + y;
}
그런데 자바스크립트 Guru로 알려진 더글러스 크락포드는 함수 생성에 있어서 함수 표현식만을 사용할 것을 권하고 있단다!!( ̄︶ ̄)↗
그 이유가 뭘까?? 🤔
console.log(add(2, 3)); // 5 출력
function add(x, y) {
return x + y;
}
console.log(add(3, 4)); // 7 출력
보다시피 함수가 선언되기 전에 함수를 호출해도 Error가 출력되지 않는다.
즉, 함수가 자신이 위치한 코드에 상관없이 함수 선언문 형태로 정의한 함수의 유효 범위는 코드의 맨 처음부터 시작한다는 것을 확인할 수 있다.
console.log(add(2, 3)); // error
// 함수 표현식 형태로 add() 함수 정의
var add = function (x, y) {
return x + y;
}
console.log(add(3, 4)); // 7
함수 표현식으로 함수를 나타내면, 함수가 선언되기 전에 호출하게되면 Error가 발생한다.
즉 더글러스는 함수 선언문으로 코드 작성 시 함수 선언문이 코드의 구조를 엉성하게 만들 수 있다는 점을 지적해 함수 표현식만을 사용하기를 권장한다
자바스크립트 엔진이 코드를 실행하기 위해선 코드에 대한 정보들이 필요하다.
코드에 선언된 변수
와 함수
, 스코프
, this
, arguments
등을 묶어, 코드가 실행되는 위치를 설명한다는 뜻의 Execution Context
라고 부릅니다.
자바스크립트 엔진은 Execution Context
를 객체
로 관리하며 코드를 Execution Context 내에서 실행합니다.
전역 Context
입니다. global object
를 생성하며 this
값에 global object
를 참조
합니다. 전역 실행 컨텍스트
는 Call Stack에 가장 먼저 추가되며 앱이 종료 될 때 삭제된다.Context
입니다. eval 함수
로 실행한 코드의 Context
이다. JS 엔진
은 생성된 Context
를 관리하는 목적의 Call Stack(호출스택)
을 갖고 있다
JS
는 단일 스레드 형식
이기 때문에 런타임에 단 하나의 Call Stack이 존재한다
JS 엔진
은 전역 범위의 코드를 실행하며 Global Execution Context
를 생성해 stack
에 push
한다.
함수가 실행 또는 종료 될 때마다 Global Execution Context
의 위로 Functional Execution Context stack
을 push(추가)
, pop(제거)
한다.
아래 gif 파일이 이해에 도움이 될 것 이다 ( ̄︶ ̄)↗
이론적인 배경이 거의 없는 상태에서 코딩을 하다 보면 머릿속으로 생각한 것 처럼 코드를 짜도 출력이 안되거나 오류가 발생하는 경우가 허다했다
그런 부분들을 조금이나마 이해하게되었고, 앞으로 코드 리뷰나 코드 해석을 할 때에 보다 구체적으로 실행 알고리즘이나 오류 발생 원인을 파악할 수 있을 것 같다.
역시 새로운 것을 배우는 건 항상 새롭고 재미있다( ̄︶ ̄)↗