JS 실행컨텍스트 : scope, hoisting, closure의 원리까지 이해하자

브리·2022년 4월 17일
0

원리가 뭐야

목록 보기
3/4
post-thumbnail

앞에서 자바스크립트 동작원리를 알아보며 자바스크립트 v8 엔진의 콜스택메모리힙에 대해 공부했었다.

이 때 간략하게 원리를 이해하기 위해 '콜스택에는 실행 함수가 들어간다' 라고 간략하게 표현했지만 실제로는 실행 컨텍스트(Execution Context) 가 들어간다.
( ex. 코드가 실행되면 global Execution Context 가 담기고 함수 A가 실행되면 함수 A의 Execution Context가 담기고,, 함수 B가 실행되면 함수 B의 Execution Context가 담기고,, 이런식 ! )

📌 실행컨텍스트 ?

코드를 실행하는데 필요한 환경을 제공하는 객체

V.E와 L.E는 환경레코드와 outer를 가지고 있는데 V.E는 컨텍스트 최초 실행 시점의 스냅샷을 유지하지만, L.E는 변경사항이 반영된다.

이 때 활성화 된 실행 컨텍스트(Running execution context)는 위에서 콜스택을 바라봤다고 생각했을 때 가장 먼저 보이는 = 가장 위에 있는 스택프레임이다.

EC의 3가지 종류

  1. Global Execution Context (전역 실행 컨텍스트)
  2. Functional Execution Context (함수 실행 컨텍스트)
  3. Eval Function Execution Context (eval 실행 컨텍스트)

📌 Record 로 JS 호이스팅 이해하기

변수의 생성원리

호이스팅을 다루기 전에 변수의 생성과정을 먼저 살펴보려고 한다.
자바스크립트에서 변수의 생성과정은 3단계로 진행된다.

1. 선언 단계(Declaration phase) : 변수를 실행 컨텍스트의 변수 객체(Variable Object)에 등록한다.
2. 초기화 단계(Initialization phase) : 변수 객체(Variable Object)에 등록된 변수를 위한 공간을 메모리에 확보한다. 이 단계에서 변수는 undefined로 초기화된다.
3. 할당 단계(Assignment phase) : undefined로 초기화된 변수에 실제 값을 할당한다.

호이스팅

변수 선언문이 최상단으로 끌어올려진 듯한 현상

물리적으로 최상단에 끌어올려지는 것이 아니라 자바스크립트 엔진이 먼저 전체코드를 스캔하면서 변수와 같은 정보들을 실행컨텍스트의 Environment Record 에 저장해놓기 때문이다.

변수 호이스팅과 함수 호이스팅

변수 호이스팅

console.log(hi); //undefined 출력
var hi="hello";
console.log(hi); // hello 출력
  • var : var로 선언을 하면 전역 컨텍스트 속 환경레코드에 hi의 값이 선언됨과 동시에 undefined 로 초기화를 시킨다.{ hi : undefined }
  • let or const : 콜스택의 전역 컨텍스트 속 환경 레코드에 선언은 되지만 초기화는 되지 않는다. 그러므로 선언 전에 호출을 하면 reference error 가 난다. { hi } (일시적 사각지대)

함수 호이스팅

  • 변수에 화살표 함수로 함수를 사용하는 경우, 함수 표현식
hi();

var hi=()=>{
	//do hi 
}

전역 컨텍스트 속 환경 레코드에는 {hi:undefined}로 선언과 초기화가 일어남. undefined는 호출될 수 없기 때문에 type error 가 발생 { hi : undefined }

hi();

const hi=()=>{
	//do hi 
}

변수 호이스팅의 let, const 와 동일하게 동작함. reference error 발생 { hi }

  • function 키워드로 사용하는 경우 , 함수 선언문
hi();
function hi(){
	//do hi
}

키워드로 함수를 선언하는 경우 선언과 동시에 환경레코드에는 study:f() 와 같이 완성된 함수 객체를 기록함. 즉, 에러없이 실행된다. 이러한 경우 선언 없이도 함수를 사용할 수 있게 되기 때문에 지양하고자 하는 목소리도 있다고 한다. { hi : f() }

📌 Outer로 JS 스코프체이닝 이해하기

Outer Environment Reference : 바깥 lexical environment 를 가리킴.

let lamp=fasle;

function goTo2F(){
	let lamp=true;
    
    function goTo3F(){
    	let pet='puppy';
        console.log(pet);//puppy
        console.log(lamp);//true
        console.log(brie);//reference error
    }
    
    goTo3F();
}

goTo2F();

이러한 경우, 자바스크립트 엔진은 lamp의 값을 무엇으로 결정할지 고민에 빠진다. 이렇게 변수나 함수의 값을 결정하는 것을 '식별자 결정' 이라고 한다.

outer는 쉽게 추상화하여 n-1층의 스택프레임과 n층의 스택프레임을 연결하는 사다리 정도로 생각할 수 있다.

위의 코드의 형태라면,
3층엔 goTo3F의 함수 컨텍스트
2층엔 goTo2F의 함수 컨텍스트
1층엔 전역컨텍스트
가 자리를 잡고 있는 3층집의 구조와 비슷해지는 것이다.

  1. console.log(pet)를 실행하면 goTo3F의 함수 컨텍스트의 레코드에서 pet의 값을 찾아서 출력한다.
  2. console.log(lamp)를 실행하면,
    (1) goTo3F의 함수 컨텍스트의 레코드에서 lamp를 찾는다 - 없음
    (2) outer 사다리를 타고 2층으로 내려가서 goTo2F에서 lamp를 찾는다 - 있으니 그 값을 출력하고 탐색을 멈춘다.
  3. console.log(brie)를 실행하면,
    (1) goTo3F의 함수 컨텍스트의 레코드에서 lamp를 찾는다 - 없음
    (2) outer 사다리를 타고 2층으로 내려가서 goTo2F에서 lamp를 찾는다 - 없음
    (3) 전역컨텍스트까지 내려와서 찾는다 - 없으므로, 참조오류를 뱉어낸다.

동일한 식별자로 인해 2번의 lamp를 출력하는 코드를 실행하면 상위 스코프에서 선언된 식별자의 값이 가려지게 되는데 이를 '변수 섀도잉' 이라고 부른다. 또한 이렇게 사다리(outer)를 통해 변수를 찾고 찾아나가는 과정 자체를 '스코프 체이닝'이라고 부른다.

📌 thisBinding

profile
무럭무럭

0개의 댓글