Javascript 코드를 어떻게 실행하는가?
클로저와 스코프는 무엇인가?
이러한 근본적인 개념을 이해하기 위해서 실행 컨텍스트(Execution Context)와 호출 스택(Call Stack)을 이해하는 것이 중요하다.
자바스크립트 엔진
1. 자바스크립트 코드를 실행할 수 있는 곳은 브라우저(Browser), Nodejs 이다.
2. 브라우저와 Nodejs는 자바스크립트 엔진을 사용해서 코드를 분석하고 실행한다.
3. 여러 엔진이 있지만 대표적으로 C/C++로 작성된 Google의 V8이 있다.
4. 각 브라우저마다 실행되는 엔진이 다를 수 있기 때문에 특정 기능이 브라우저에서 지원되지 않는지 이해할 수 있다.
5. 예를 들면 ES6기능을 사용하고 싶을 때 IE는 안되고 Chrome은 되는 이유가 여기에 있다.
실행 컨텍스트 ( Execution Context )
1. 브라우저는 우리가 애플리케이션에서 작성하는 high-level의 자바스크립트 코드를 기본적으로 이해하지 못한다.
2. 이 코드는 브라우저와 컴퓨터가 이해할 수 있는 형식, 즉 기계 코드로 변환되어야한다.
3. 브라우저가 HTML을 읽다가 <script> 태그나 onClick처럼 자바스크립트 코드를 포함하는 속성을 통해 실행해야 할 코드를 만나면, 그 코드를 자신의 자바스크립트 엔진으로 보낸다.
4. 브라우저의 자바스크립트 엔진은 이 자바스크립트 코드의 변환과 실행을 처리하기 위한 특별한 환경을 생성한다.
5. 이 환경을 실행 컨텍스트라고 한다.
6. 실행 컨텍스트의 실행 시간 동안 구체적인 코드는 파서에 의해 구문 분석되고, 변수와 함수는 메모리에 저장되며, 실행 가능한 바이트 코드가 생성되고 코드가 실행된다.
7. 자바스크립트에는 두 가지 중요한 실행 컨텍스트가 있다.
- 글로벌 실행 컨텍스트 ( Global Execution Context )
- 함수 실행 컨텍스트 ( Function Execution Context )
글로벌 실행 컨텍스트
1. 자바스크립트 엔진이 브라우저 내부나 Node.js에서 자바스크립트 파일을 해석하기 시작할 때의 기본 실행 컨텍스트이다.
2. 글로벌 실행 컨텍스트는는 단 하나뿐이며 global object와 this로 구성된다.
3. 브라우저 내에서 자바스크립트를 실행하는 경우 글로벌 객체는 window가 된다.
4. nodejs 환경에서는 global로 설정된다.
5. this는 항상 글로벌 객체를 가리킨다.
실행 컨텍스트의 생성단계와 실행 단계
console.log(firstName);
console.log(lastName);
console.log(getName);
var firstName = "cactus";
var lastName = "log";
getName();
function getName() {
console.log(firstName + lastName);
}
undefined
undefined
function getName() {
console.log(firstName + lastName);
}
"cactuslog"
1. 위의 코드를 보면 선언하기 전에 log 출력을 위해 변수와 함수에 접근하였는데 에러가 발생하지 않는다.
2. 위의 현상을 이해하기 위해 생성단계와 실행 단계에서 무엇을 하는지 알아보자.
생성 단계
1. 글로벌 객체와 this를 생성한다.
2. 변수와 함수를 위한 메모리를 설정한다.
3. 모든 변수를 undefined로 초기화하고 함수 선언을 메모리에 저장한다.
실행 단계
1. 자바스크립트 엔진이 코드를 줄별로 탐색하여 실행하고 이미 메모리에 있는 변수에 실제 값을 할당한다.
2. 위의 코드를 실행할 때 firstName, lastName 변수에 undeifned로 초기화 되었기 때문에 log가 찍힌다.
3. 함수 또한 정의에 대해 메모리가 생성 단계에서 할당 되므로 실행 단계에서 log에 찍힌다.
변수와 함수의 선언이 아래에 있지만 생성 단계에서 변수를 undefined로 초기화하고 함수 선언을 메모리에 저장하는 것을 호이스팅 ( hoistng )이라고 한다.
함수 실행 컨텍스트
1. 함수가 호출될 때마다 자바스크립트 엔진은 그 함수 내의 코드를 분석하고 실행하기 위해 함수 실행 컨텍스트를 생성한다.
2. 함수 실행 컨텍스트는 글로벌 실행 컨텍스트 내부에 존재하며 글로벌 실행 컨텍스트의 변수에 접근할 수 있다.
3. 함수 실행 컨텍스트는 생성단계에서 다음을 수행한다.
ㄱ. arguments 객체 생성
ㄴ. this 객체 생성
ㄷ. 변수와 함수를 위한 메모리 설정
ㄹ. 모든 변수를 undefined로 초기화 하고 함수 선언을 메모리에 저장
예제를 통해 정리해 보자
글로벌 실행 컨텍스트의 생성 단계에서 다음을 수행한다.
1. 글로벌 객체 생성
2. this 객체 생성
3. 변수와 함수에 대한 메모리 설정
4. 모든 변수를 undefined로 초기화하고 함수 선언을 메모리에 저장
실행 단계에서 firstName에 ”cactus”가 할당된다.
함수가 호출 될 때 getName(), 함수 실행 컨텍스트가 생성되고 다음을 수행한다.
1. arguments객체 생성
2. this 객체 생성
3. 지역 변수 lastName를 위한 메모리 설정 후 undefined로 초기화
4. 지역변수 prefix에 argument “hi”를 바로 할당
1. 지역변수 lastName에 “log”를 할당한다.
2. firstName은 함수 실행 컨텍스트에는 없지만 글로벌 실행 컨텍스트에 존재하므로 접근 가능하다.
1. 함수 실행 컨텍스트는 함수 실행 후 제거된다.
2. 따라서 지역변수 lastName은 함수가 실행되는 동안에만 존재한다.
실행 컨텍스트를 알면 Scope와 Closure를 이해할 수 있다.
스코프 ( Scope )
함수 실행 컨텍스트는 scoping이라는 과정을 통해 정의한 변수와 함수를 접근할 수 있는 환경인 자신만의 스코프를 생성한다.
간단한 예제를 보자
function fn() {
var age = 10;
}
fn();
console.log(age); // Error
1.변수 age는 fn 함수 실행 컨텍스트 내부의 지역변수이다.
2. 따라서 age의 스코프는 오직 fn 함수 내부에만 존재한다.
3. 함수 실행 컨텍스트는 함수 실행 후 제거 되기 때문에 더이상 age에 접근할 수 없다.
좀 더 복잡한 예제를 보자
var firstName = "cactus";
var lastName = "log";
function changeName() {
var firstName = "newCactus";
lastName = "logger";
}
changeName();
console.log(firstName); // "cactus"
console.log(lastName); // "logger"
1. changeName 함수를 호출 하는 순간 firstName을 지역변수로 가지는 함수 실행 컨텍스트가 생성된다.
2. 주의할 점은 글로벌 스코프의 firstName과 다르다는 것이다.
3. changeName 함수는 또한 lastName에 값을 할당하지만 함수 실행 컨텍스트에는 lastName이 없다.
4. 자바스크립트 엔진은 스코프에서 변수를 찾지 못하면 부모 스코프를 확인하고 글로벌 스코프에 도달할 때까지 계속 상위를 탐색한다.
5. lastName은 글로벌 스코프에 존재하고 이 값이 “logger”로 변경된다.
6. 이렇게 자바스크립트 엔진이 로컬 실행 컨텍스트에 변수가 존재하지 않을 경우 하나씩 부모 실행 컨텍스트를 확인하는 과정을 스코프 체인이라고 한다.
클로저 ( Closure )
1. 클로저는 자바스크립트에서 내부 함수가 외부(둘러싸는) 함수의 변수에 접근할 수 있는 기능이다.
2. 내부 함수는 둘러싸는 함수가 실행될 때의 스코프 체인을 보존하므로, 둘러싸는 함수의 변수에 접근할 수 있다.
3. 클로저를 통해 내부 함수는 외부 함수의 실행이 완료된 후에도 외부 함수의 변수에 접근할 수 있다.
예시를 보자
function outer() {
var a = 10;
return function inner(point) {
return a + point;
}
}
var result = outer()(20);
console.log(result); // 30;
1. outer 함수의 반환 값은 inner 함수이다.
2. outer 함수를 호출하면 inner 함수를 반환하고 종료한다.
3. inner 함수는 outer 함수 호출이 종료되었음에도 자신의 외부 스코프인 outer 함수의 변수 a에 접근이 가능하다.
호출 스택 (Call Stack)
1. 실행 스택이라고도 하며 스크립트의 생명 주기 동안 생성된 모든 실행 컨텍스트를 추적한다.
2. 자바스크립트는 단일 스레드 언어로, 한 번에 하나의 작업만 실행할 수 있다.
3. 따라서 다른 액션, 함수, 이벤트가 발생할 때마다 각 이벤트에 대해 하나의 실행 컨텍스트가 생성된다.
4. 단일 스레드 특성 때문에, 실행될 실행 컨텍스트들이 쌓이는 스택이 생성되며, 이를 호출 스택이라고 한다.
5. 스크립트가 브라우저에서 로드되면, 글로벌 컨텍스트가 기본 컨텍스트로 생성되어 자바스크립트 엔진이 코드를 실행하기 시작하는 곳이며 호출 스택의 가장 아래에 위치한다.
6. 그런 다음 자바스크립트 엔진은 코드에서 함수 호출을 찾는다.
7. 각 함수 호출마다 해당 함수에 대한 새로운 함수 실행 컨텍스트가 생성되어 현재 실행 중인 실행 컨텍스트 위에 위치한다.
8. 실행 스택 상단의 실행 컨텍스트가 활성 실행 컨텍스트가 되며, 자바스크립트 엔진에 의해 항상 먼저 실행된다.
9. 활성 실행 컨텍스트 내의 모든 코드 실행이 완료되면, 자바스크립트 엔진은 해당 함수의 실행 컨텍스트를 실행 스택에서 제거하고 그 아래에 있는 다음 컨텍스트로 이동한다.
다음 예제를 보자.
var base = 100;
function first(x) {
var y = 3;
return second(x * y);
}
function second(b) {
var a = 10;
return a + b + base;
}
first(5); //125
1. 자바스크립트 엔지은 글로벌 실행 컨텍스트를 생성하고 호출 스택에 푸시한다.
2. base 변수는 글로벌 실행 컨텍스트에 속해 있다.
1. 자바스크립트 엔진이 함수 호출을 만나면 first(5), 새로운 함수 실행 컨텍스트가 생성된다.
2. 이 새로운 컨텍스트는 현재 컨텍스트인 글로벌 실행 컨텍스트 상단에 푸시된다.
3. first 함수에서 변수 y = 3이 저장되고 x * y 연산 실행 후 second 함수가 호출 된다.
4. 단일 스레드 특성으로 인해 first 함수의 실행은 일시 중단된다.
1. second 함수에 대한 새로운 함수 실행 컨텍스트가 생성되고 스택의 상단에 위치시켜 활성 컨텍스트로 만든다.
2. 변수 a = 10이 저장되며 넘어온 x * y 결과인 b와 전역변수 base를 더한 결과를 반환한다.
1. second 함수의 모든 작업을 수행했으므로 실행 컨텍스트를 pop 제거 한다.
2. 다시 first 함수의 실행 컨텍스트가 활성 컨텍스트가 된다.
1. first 함수는 second 함수의 반환값을 다시 반환하고 종료된다.
2. first 함수의 실행 컨텍스트가 pop 된다.
3.마지막으로 전체 코드의 실행이 완료되면 글로벌 실행 컨텍스트가 제거된다.
'javascript' 카테고리의 다른 글
nullish 병합 연산자 ?? 에 대하여 (1) | 2024.03.24 |
---|---|
number 와 bigint에 관하여 (feat. nestjs) (0) | 2024.03.14 |
자바스크립트 Set를 활용하여 중복을 제거하자! (2) | 2024.03.07 |