execution context
실행 컨텍스트는 실행할 코드가 필요로 하는 여러가지 정보들을 모아둔 환경이다.
자바스크립트의 실행 컨텍스트는 크게 전역 실행 컨텍스트와 함수 실행 컨텍스트로 나뉜다.
자바스크립트를 실행하면 자바스크립트의 call stack 안에 전역 실행 컨텍스트가 담기고 함수를 실행할 때마다 해당 함수의 실행 컨텍스트가 call stack에 담긴다.
다음 코드를 실행했을 때 call stack의 변화를 보자.
function A(){
function B(){
console.log("B");
}
console.log("A");
B();
}
A();

- 처음 코드가 실행되면 전역 실행 컨텍스트가 call stack에 등록된다.
- 함수 A를 실행하면 함수 A 실행 컨텍스트가 call stack에 등록된다.
- 함수 B가 실행되면 함수 B 실행 컨텍스트가 call stack에 등록된다.
- 함수 B가 종료되면 함수 B 실행 컨텍스트가 call stack에서 제거된다.
- 함수 A가 종료되면 함수 A 실행 컨텍스트가 call stack에서 제거된다.
- 자바스크립트 코드가 종료되면 전역 실행 컨텍스트가 call stack에서 제거된다.
자바스크립트 호이스팅
호이스팅을 이해할 때 아래 실행 컨텍스트의 구조를 보면서 이해를 하는 것을 추천한다.

우선 실행 컨텍스트의 environment records에는 변수에 대한 정보가 담긴다.
컨텍스트에 변수를 저장한다의 의미는 해당 컨텍스트의 lexical environment의 environment records에 변수를 저장한다고 보면 된다.
console.log(a);
var a = "hello"
출력
undefined
var로 변수를 선언하고 그 이전에 해당 변수를 호출하면 오류가 발생하지 않고 undefined로 출력된다.
자바스크립트가 전체 코드를 스캔 후 변수 a에 undefined를 할당해서 실행 컨텍스트에 미리 저장했기 때문이고 이러한 현상을 hosting이라고 한다.
var가 아닌 const, let을 사용하면 변수 a 정보를 실행 컨텍스트에 담긴 하지만 할당하지 않기 때문에 에러가 발생한다.
console.log(a);
const a = "hello"
ReferenceError: Cannot access 'a' before initialization
자바스크립트 함수는 1급 함수이기 때문에 변수와 비슷하게 사용할 수 있다.
즉 함수에서도 호이스팅 현상이 발생할 수 있다.
a();
var a = () => {
console.log('A');
}
위 코드를 실행하면 TypeError: a is not a function 오류가 발생한다.
역시 변수 a에 undefined를 할당하여 실행 컨텍스트에 저장했기 때문에 undefined는 함수가 아니라는 오류가 발생하는 것이다.
이번에도 var를 const로 바꿔보자.
a();
const a = () => {
console.log('A');
}
ReferenceError: Cannot access 'a' before initialization 오류가 발생한다.
변수 a를 실행 컨텍스트에 저장하긴 하지만 할당을 하지 않았기 때문에 참조 에러가 발생하는 것이다.
하지만 화살표 함수를 사용하지 않고 function을 통해 함수를 선언한다면 실행 컨텍스트에 해당 함수 정보를 담기 때문에 선언 위치와 상관없이 함수를 사용할 수 있다.
a();
function a() {
console.log('A');
}
출력
A
scope
스코프란 변수에 접근할 수 있는 범위를 의미한다.
크게 어디서든 접근할 수 있는 전역 스코프와 해당 지역에서만 접근할 수 있는 지역 스코프로 나뉜다.
자바스크립트는 함수를 선언할 때마다 새로운 스코프가 생성된다.
const a = "A";
function A(){
const a = "AA";
console.log(a);
}
A();
함수 A 안에 지역 스코프가 존재하고 그 안에 a라는 변수가 존재한다.
위 코드의 결과는 A가 출력되는게 아니라 AA가 출력된다.
이 과정은 아래 식별자 결정에서 자세하게 다뤄볼 예정이다.
자바스크립트에서는 함수 스코프외에 블록 스코프란 것도 존재한다.
const a = "A";
{
const a = "AA";
console.log(a);
}
console.log(a);
블록을 생성할 때 블록 스코프가 만들어지고 위 코드의 결과는 AA 다음에 A가 출력된다.
참고로 ES2015 부터 const, let 키워드가 생겼고 const, let 키워드를 통해 블록 스코프를 생성할 수 있다.
var의 경우 함수 스코프만 지원하기 때문에 아래 코드의 결과는 둘 다 모두 AA가 출력된다.
var a = "A";
{
var a = "AA";
console.log(a);
}
console.log(a);
식별자 결정
아래 코드처럼 동일한 이름을 가진 변수가 여럿 있으면 어떻게 판단할까?
const a = "A";
function A(){
const a = "AA";
console.log(a);
}
A();
우선 전역 실행 컨텍스트에 변수 a에 대한 정보가 저장되어 있을 테고 함수 A 실행 컨텍스트에도 변수 a에 대한 정보가 저장되어 있을 것이다.
위에서 본 실행 컨텍스트 구조를 다시 보자.

실행 컨텍스트의 lexical environment 안의 outer reference environment라는 것이 존재한다.
이는 이전 실행 컨텍스트를 가리키기 때문에 이전 실행 컨텍스트를 참조할 수 있게 된다.
다시 위 코드를 보자.
const a = "A";
function A(){
const a = "AA";
console.log(a);
}
A();
함수 A 내부 실행 컨텍스트에 변수 a에 대한 정보가 있기 때문에 AA를 출력하게 된다.
다음 코드를 보자.
const a = "A";
function A(){
console.log(a);
}
A();
위 코드에서는 함수 A의 실행 컨텍스트에 변수 a에 대한 정보가 없다.
이 경우 outer reference environment를 통해 이전 컨텍스트로 돌아가서 변수 a에 대한 정보를 찾는다.
전역 컨텍스트에는 변수 a에 대한 정보가 있기 때문에 A가 출력된다.
이렇게 동일한 이름의 변수가 여럿 있을 때 값을 가져오는 과정을 식별자 결정이라고 한다.
그리고 스코프들의 연결 리스트인 스코프 체인을 통해 계속 스코프를 찾는 과정을 스코프 체이닝이라고 한다.
클로저
클로저는 함수와 렉시컬 환경의 조합으로 함수가 생성될 당시의 외부 변수를 기억한다.
다음 코드를 보면서 클로저에 대해 이해해보자.
function makeCounter() {
let count = 0;
return function() {
return count++;
};
}
let counter = makeCounter();
console.log(counter())
console.log(counter())
console.log(counter())
makeCounter 렉시컬 환경에 생성되고 counter라는 함수는 makeCounter의 렉시컬 환경으로 되돌아갈 수 있다.

counter를 한 번 실행하면 makeCounter 실행 컨텍스트를 가리키는 익명 함수 실행 컨텍스트가 생성되고 이 안에는 변수 count에 대한 정보가 없기 때문에 계속 makeCounter 실행 컨텍스트로 되돌아가 count 정보를 가져온다.
counter를 계속 실행하면 계속 익명 함수 실행 컨텍스트가 생성되고 계속 makeCounter 렉시컬 환경 안의 count 정보를 가져오는 것이다.
위 코드의 결과는 아래와 같다.
출력
0
1
2
참고
https://blog.naver.com/dlaxodud2388/222655214381
[JavaScript] ES6의 Execution Context의 동작 방식과 Lexical Nesting Structure
ES5를 기점으로 Execution Context의 동작하는 방식과 구조가 많이 달라졌다. 이 블로그의 예전 포스팅...
blog.naver.com
https://medium.com/@su_bak/javascript-%EC%8A%A4%EC%BD%94%ED%94%84-scope-%EB%9E%80-bc761cba1023
스코프란?
자바스크립트를 공부할 때 스코프란 단어를 많이 접할 수 있는데요. 이 스코프란 무엇인지에 대해 알아보겠습니다.
medium.com
How will the Lexical environment and the Variable Environment will look like at the following code
I have been looking at ecma262 and I've seen that when we invoke a function we are basically calling the internal method [[Call]] and while I was looking at the steps of the [[Call]] I have found t...
stackoverflow.com
https://www.youtube.com/watch?v=EWfujNzSUmw
https://www.youtube.com/watch?v=tpl2oXQkGZs
https://ko.javascript.info/closure
변수의 유효범위와 클로저
ko.javascript.info
댓글