실행컨텍스트
- 실행할 코드에 제공할 '환경 정보'를 모아둔 객체
- 이것을 콜스택에 쌓아 올렸다가, 가장 위의 컨텍스트와 관련있는 환경 정보를 수집 후, 코드들을 실행
- 종류
- 전역 실행 컨텍스트 : 자동 생성
- 함수 실행 컨텍스트 : 우리가 흔히 실행 컨텍스트를 구성하는 방법 = 함수 호출 = 함수 실행 (함수 선언X)
- eval 실행 컨텍스트
콜스택 = 실행 대상임
각각의 실행 컨텍스트에 담기는 정보
- VariableEnvironment : 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보 , 선언 시검의 LexicalEnvironment의 스냅샷(변경사항 반영X)
- environmentRecord : 정보 수집 -> 호이스팅
- outerEnvironmentReference : 스코프 체인
- LexicalEnvironment : VariableEnvironment 를 복사 -> 변경사항 반영ㅇ
- ThisBinding : this 식별자가 바라봐야할 대상 객체
LexicalEnvironment
(어휘적) 사전적인 환경 : 컨텍스트를 구성하는 환경 정보를 모아둠
environmentRecord: 현재 컨텍스트와 관련된 코드의 식별자 정보 저장
식별자 정보
- 함수에 지정된 매개변수 식별자
- 선언한 함수 그 자체
- var로 선언한 변수의 식별자
=> 코드가 실행되기 전에 JS엔진은 이미 해당 환경에 속한 코드의 변수명을 모두 알고 있다.
<=> 식별자를 모두 최상단으로 끌어올려놓고나서 실제 코드를 실행한다고 편의상 개념을 도입
: 호이스팅 (식별자만ㅇ, 어떤 값을 할당했는지는 관심X)
▶️ 호이스팅이란 실행컨텍스트의 Lexical Envirionment 의 environmentRecord 가 함수 실행 전에 식별자 정보를 수집하는 과정을 추상화한 개념으로, 실행 컨텍스트가 관여하는 코드 집단의 최상단으로 식별자를 끌어올린다고 해석함
function a (){
console.log(b); // --- (1) 에러 또는 undefined가 출력될거 같지만?
var b = 'bbb';
console.log(b); // --- (2)
function b (){}
console.log(b) // --- (3)
}
▼ 호이스팅
function a (){
var b; // 호이스팅 (변수는 선언부만)
function b (){ // 함수 선언 전체 호이스팅
};
console.log(b); // --- (1) => function b(){}
b = 'bbb';
console.log(b); // --- (2) => 'bbb'
console.log(b); // --- (3)
}
함수 선언문은 함수명으로 선언한 변수에 함수를 할당한 것 처럼 여길 수 있다.
function a (){
var b;
var b = function b (){}; // 변수에 함수를 할당
console.log(b); // --- (1) => function b(){}
b = 'bbb';
console.log(b); // --- (2) => 'bbb'
console.log(b); // --- (3) => 'bbb'
}
함수 선언문 vs 표현식
// 선언문
function a (){ ... }
// 표현식
// 익명 함수 표현식
var b = function (){ ... } // b()로 함수 호출
// 기명 함수 표현식
var c = function d(){ ... } //c()로 함수 호출, d()는 함수 내부에서만 호출ㅇ. 외부에서는 호출X
예제 )
console.log(sum(1,2));
console.log(mutiply(3,4));
function sum (a,b){ //선언문
return a+b;
}
var multiply = function (a,b){ //표현식
return a*b;
}
▼호이스팅
function sum (a,b){ //선언문 : 전체 호이스팅
return a+b;
}
var multiply; // 표현식 : 선언부만 호이스팅
console.log(sum(1,2)); // 3
console.log(mutiply(3,4)); // not a function 에러 : 런타임 종료
multiply = function (a,b){ //표현식
return a*b;
}
표현식: 함수를 하나의 값으로 취급할 수 있다.
상대적으로 표현식이 안전하다?
동명의 함수가 함수 선언식으로 되어있을때
console.log(sum(3, 4))
function sum (x, y){
return x + y;
}
var a = sum(1,2);
// -------- 나중에 추가된 코드
function sum (x, y){
return x + ' + ' + y + ' = ' + (x+y);
}
var c = sum(1, 2)
console.log(c)
함수 선언식은 전체가 호이스팅 되므로 추가된 코드 위에 작성된 코드도 모두 나중에 선언된 sum으로 실행됨
스코프, 스코프 체인, outerEnvironmentReference
스코프 = 식별자의 유효 범위
- 전역 스코프
- 함수 스코프
- 블록 스코프 (ES6 ~)
스코프 체인 : 식별자의 유효 범위(스코프)를 안에서부터 바깥쪽으로 검색해 나가는 것
A 외부에서 선언한 변수는 A 내부에서 접근 가능하지만,
A 내부에서 선언한 변수는 오직 A 내부에서만 접근할 수 있고 A 외부에서 접근할 수 없다.
: 함수 자신이 선언될 당시의 LexicalEnvironment를 참조한다 (가장 가까운 요소부터 차례로 접근)
=> 여러 스코프에서 동일한 식별자를 선언한 경우 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자만 접근 가능
=> outerEnvironmentReference 가 있기에 가능함
*선언 = 어떤 실행 컨텍스트가 활성화된 상태에서 일어남
var a = 1;
function outer(){
function inner(){
console.log(a);
var a = 3;
}
inner();
console.log(a);
}
outer();
console.log(a);
코드 | 전역 컨텍스트 | outer 실행 컨텍스트 | inner 실행 컨텍스트 | outer | 전역 |
---|---|---|---|---|---|
var a = 1; | 전역 컨텍스트가 콜스택에 담긴다. (활성화) (브라우저에서 자바스크립트 파일이 열리는 순간) |
||||
function outer(){ | |||||
function inner(){ | |||||
console.log(a); | a = undefined 출력 (변수는 호이스팅 되지만 값이 할당되기 전) |
||||
var a = 3; | a 할당 / inner 컨텍스트 종료 (이 코드가 없다면 outer 함수의 LexicalEnvirionment 참조 => a = 1출력) |
||||
inner(); | inner함수 실행 => inner 함수 실행 컨텍스트 생성/활성화 |
||||
console.log(a); | a = 1 출력 outer 선언 당시 |
||||
outer(); | outer함수 실행 => outer 함수 실행 컨텍스트 생성 (outer에 대한 환경정보 수집) |
||||
console.log(a); | a = 1출력 |
LexicalEnvironment
전역 컨텍스트 L.E
environmentRecord : a, outer
outer 컨텍스트 L.E
environmentRecord : inner
outerEnvironmentReference : Global L.E 를 참조
inner 컨텍스트 L.E
environmentRecord : a
outerEnvironmentReference : outer L.E 를 참조
- inner -> outer -> 전역 컨텍스트 : 스코프 체인을 타면서 접근 가능한 변수의 수가 늘어남
- inner 안에 a 가 선언되어 외부의 a에는 접근 불가함 (은닉)
스코프 체인 상의 모든 변수에 접근 가능한것은 아님. 스코프 체인 상 첫번째 인자 (inner 스코프의 LE) 부터 검색하고, 거기서 a가 반환되면 더이상 스코프 체인 검색을 진행하지 않음 - console.dir( ) 로 상위 스코프 정보를 확인 가능함
- 디버거 이용
전역 변수 vs. 지역 변수
- 전역 컨텍스트의 LE에 답긴 변수 = 전역 변수
- 그 밖의 함수에 의해 생성된 실행 컨텍스트의 변수 = 지역 변수
원문 그대로 사용한 용어를 사용하다보니 처음 읽었을때 뭐가 뭐였지 하고 헷갈리기도 하고 이해하기가 조금 어려웠다.
포스팅을 하면서 한번 더 봤더니 조금 익숙해진것같다.
호이스팅이 단순히 코드를 위로 끌어올리는 것이라고만 알고있었는데, 자바스크립트 엔진의 변수와 함수를 처리하는 방법과 주체 등의 동작 원리를 이해할 수 있었다.
모의 면접 질문에 대한 답을 다시 해보자
- 중복선언
var 중복선언 가능, let,const 중복선언 안됨. var 중복선언하면 오버라이드 되어 이전 값은 덮어씌워진다.
- 스코프? var는 전역스코프, let,const 블록 스코프 => if, for, while 같은 블록 안에서 선언될 경우 블록 밖에서 접근 불가
var는 함수 스코프이다. 블록 {}을 무시한다.
선언된 함수 내에서만 접근할 수 있다. 함수 바깥에서 선언하면 전역 스코프이다.
- var변수는 어디에서 접근 가능한가? 호이스팅이 일어나서 undefined가 할당되서 접근 가능하다.
선언된 컨텍스트 내부, 또는 그 내부의 함수 스코프에서도 접근이 가능하다. (동명의 변수명을 갖지 않는다면)
- 호이스팅은 모두 일어난다. let,const 선언하기 전에 변수에 접근을 하면 에러가 발생함.잘 모르지만, 선언과 할당에 시점이 차이가 있다.
변수 선언부만 호이스팅 됨. 할당은 실제 코드에서 일어남.
- 호이스팅의 정의란? 마치 맨위에 코드가 작성된 것 처럼 선언된 것 처럼 끌여올려진 것
- 호이스팅이 되면 어떤 현상이 일어나는지? var는 선언은 되지만 할당이 되지 않았기 때문에 값에 접근하려고하면 undefined를 반환한다.
변수의 선언부, 선언식 함수 전체가 맨 위로 끌여올려진다.
- 호이스팅이 일어나는 원인? 잘 모르겠다.
실행 컨텍스트의 LexicalEnvironment 의 environmentRecord가 식별자(변수, 함수) 정보를 수집하기 때문에 함수를 실행하기 전부터 정보를 알고 있다.
- 실제로 코드가 위로 올라가는지? 변수선언을 먼저 해석하기 때문에 실제로 코드가 위로 올라간다고 할 수 있을 것 같다.
실제로 코드가 위로 올라가는것은 아님. 자바스크립트 엔진이 선언 부분을 미리 처리하는 방식
이 포스팅은 <코어 자바스크립트> 를 읽고 내용을 정리한 포스팅입니다.
틀린 내용이 있다면 알려주세요 _ _
'개발 공부 일지 > JavaScript' 카테고리의 다른 글
[프로그래머스] 기초 - 전국 대회 선발 고사 (0) | 2025.03.07 |
---|---|
[프로그래머스] 기초 - 배열의 길이를 2의 거듭제곱으로 만들기 (0) | 2025.03.05 |
이벤트 핸들러를 할당하는 시점 (1) | 2025.02.07 |
DOM 요소 자체를 변수에 저장하자 (1) | 2025.02.04 |
함수에서 this 바인딩 (2) | 2025.02.01 |