Slack의 학습소통에서 다른 수강생분의 이러한 질문에서 다시 한번 실행컨텍스트를 정리하고
그 안에서 호이스팅이 어떻게 진행되는지를 정리해보고자 합니다.
Javascript는 browser마다 다른 Javascript엔진에 의해 해석된다.
그럼 어떤 방식으로 해석을 하게 될까??
Javascript는 먼저 Callstack이라는 공간에
전역, 함수단위로 실행컨텍스트를 형성하고 빠져나가게된다.
결국 전역 실행 컨텍스트는 Callstack 가장 밑부분에서 모든 코드가 해석될 때까지 있다가
마지막에 빠져나가게 된다.
function func1() {
function func2() {
console.log('func2 호출');
}
func2();
console.log('func1 호출');
}
func1();
실행컨텍스트는 코드가 실행되는 환경이라고 생각하면된다.
함수는 가장 맨위에 존재하는 컨텍스트가 활성화된 컨텍스트이고 맨위부터 함수가 실행된다.
위 컨텍스트 안에서 코드가 실행되고 컨텍스트가 빠져나가는 방식이다.
이렇게 컨텍스트가 형성이 될 때 저장이 되는 정보 3가지가 있다.!
컨텍스트가 형성되면서 저장이 되는 정보 3가지
1. 변수정보
2. 스코프체인(이따함)
3. this정보
이렇게 정보3가지를 컨텍스트가 형성이 되면서 "환경레코드" 또는 "환경정보"에 담기게 된다.
이러한 특징 때문에 호이스팅현상이 발생하게 된다.
이렇게 컨텍스트가 생성이 되면서 위 3가지 정보를 환경레코드에 저장을 하는게 생성단계
저장 후에 코드를 본격적으로 실행하는 단계를 실행단계 라고한다.
다음과 같은 코드를 자바스크립트는 어떻게 이해할까
//전역
console.log(number)
var number = 100
참조에러가 발생할거 같지만 그렇지 않다.
undefined가 출력이된다.
그 이유는
1. 전역에서 전역실행컨텍스트가 형성이되고
2. 전역실행컨텍스트가 형성이 될 때 변수에대한 정보를 환경레코드에 저장을 먼저한다.(생성단계)
3. console.log()를 찍었을 때 환경 레코드에 담긴 변수에 대한 정보 undefined를 출력한다.(실행단계)
그럼 왜 100이 출력이 되는게 아니라 undefined가 출력이 될까?
변수에 대한 정보는 정확하게는 선언, 초기화된 변수를 기록해준다.
//전역
console.log(number)
var number = 100
위 코드에서 number라는 식별자를 메모리공간에 연결해주고 var로 선언했기 때문에 초기화가 동시에 일어나
undefined라는 값이 바인딩이 되어진다.
이러한 과정이 생성단계 즉, 실행컨텍스트가 형성될 때 일어난다(코드가 실행되기전)
그치만 변수타입에 따라 초기화가 이루어지지 않을 수 있고 이러한 경우에 참조에러가 발생한다.
위 같은 특징 때문에 호이스팅이 발생한다.
호이스팅이란 물리적으로 변수가 최상단으로 올라온 것이 아닌
변수가 맨 위로 올라온 것처럼 보이는 현상을 말한다.
const 와 let은 실행컨텍스트가 형성이 될 때 선언은 일어나지만 초기화는 일어나지 않는다.
그래서 만약 변수의 사용시점이 선언 시점보다 늦는다면 참조에러가 발생하게 된다.
console.log(num)
const num=100
let도 동일하다.
그럼 var는 어떨까
var는 선언과 초기화가 둘다 일어나게 된다.
만약 변수의 사용시점이 선언 시점보다 늦는다면 참조에러는 발생하지 않고 undefined를 출력한다.
결국 Const,let,var 모두 호이스팅은 일어나는게 맞다
numF()
var numF=()=>{
console.log("숫자")
}
위 Const, let, var와 동일한 호이스팅 구조를 갖는다.
즉, const,let 으로 선언하게되면 초기화는 이루어지지 않고
var로 선언하게되면 초기화는 이루어진다.
numF()
function numF(){
console.log("숫자")
}
함수 선언문은 선언과 동시에 온전히 함수 레코드에 저장이된다.
정리해보면
- 함수의 표현식은 const, let, var 중 어떤 변수타입으로 선언하느냐에 따라 초기화가 이루어지고
그 결과 각 호이스팅의 결과가 다르게 보여진다.- 함수의 선언문은 선언과 동시에 실행컨텍스트가 환경레코드에 저장하기 때문에 어디에서 선언이 되든
가장 맨위로 올라온듯한 사용이 가능하다.
아까 실행컨텍스트가 생성될때 환경레코드에 3가지 정보가 저장된다고 했다.
2번 스코프체이닝을 정리해보자
간단하다 그냥 바깥쪽컨텍스트에서 안쪽컨텍스트로 변수정보를 찾아서 그 체이닝을 기록해준다.
function func1() {
let number =200
function func2() {
console.log(number)
}
func2();
console.log('func1 호출');
}
func1();
위 코드와 그림에서 step3의 실행컨텍스트가 형성이 될때 number에 대한 식별자를 결정해주는 체이닝을
스코프 체이닝 이라고한다
func2 컨텍스트에 number라는 변수는 해당 스코프의 컨텍스트에서 찾지못했고
fun1에 있는 number를 찾아 식별자를 결정해주는 스코프 체이닝이 실행컨텍스트가 형성될때 환경레코드에 저장된다.
그럼 모든 스코프에 있는 변수는 전역에 있는 변수를 참조할 수 있게 된다.(같은 네이밍의 변수라면)
그래서 전역에 변수를 선언하는 것을 지양하는거라 생각된다.
This는 동적으로 바인딩된다고 알고있다.
그런데 전역실행컨텍스트가 형성될 때 this에 대한 정보가 저장이 된다는 건
아직 함수가 실행전이라 This에 대한 정보를 모르는 거 아닌가?
라는 생각을 했다.
학습을 해보니
결국 전역실행컨텍스트가 형성이 될 때 this를 누가 호출하는지를 인지하고 this에 대한 값을 이미 바인딩 해놓는다고한다.
그럼 실행단계에서 해당 바인딩된 This의 값을 호출해 사용한다고한다.
This가 바인딩되는 규칙은 다음과 같다.
- 전역컨텍스트=> this는 전역 객체 (window)
- 함수호출시 => this는 전역객체 또는 undefined
- 메서드호출시 => this는 해당 메서드가 속한 객체
- 생성자함수호출시 => this는 생성자 함수 내에서 새롭게 생성되는 인스턴스
- eventlistneer => this는 이벤트를 발생시킨 요소