《 실행 컨텍스트 》과 연관된 내용이 많으므로 같이 공부하도록 하자!
스코프(scope)란?
예시
var x
가 함수 밖(전역 변수) 에서 1번 함수 안(지역 변수)에서 1번 참조된 것을 알 수 있다.var x = "global";
function test() {
var x = "local";
console.log(x); // "local";
}
test();
console.log(x); // "global";
➡️ 하지만, var x
가 어디있는지에 따라 동일한 코드도 다른 결과를 만들어 낸다.
➡️ 그 이유는, 이름은 동일한 식별자이지만 스코프가 다르기 때문이다.
➡️ 즉, 위의 코드를 통해 다른 스코프에서는 같은 이름의 식별자를 사용할 수 있다는 것을 알 수 있다.
스코프가 없으면 어떻게 될까?
스코프라는 개념이 없다면 같은 이름을 갖는 변수는 충돌을 일으킬 것이다.
변수가 어느 위치에 선언되어있는지에 따라 Global Scope(전역 스코프)와 local Scope(지역 스코프)로 나뉜다.
// Global Scope
var x = "global";
// local Scope
function test() {
var x = "local";
console.log(x); //1
}
전역범위에서 변수 선언시 어디서든지 접근할 수 있다.
// global scope에서의 변수 선언
var varvalue = "Happy!";
let letvalue = "Happy!";
const constvalue = "Happy!";
// 일반적인 접근
console.log(varvalue);
console.log(letvalue);
console.log(constvalue);
// 함수에서의 접근
function print_value() {
console.log(varvalue);
console.log(letvalue);
console.log(constvalue);
}
print_value();
// 조건문에서의 접근
if (true) {
console.log(varvalue);
console.log(letvalue);
console.log(constvalue);
}
함수 범위에서 변수 선언시 해당 함수 내부에서만 접근 가능하다.
*lacal(지역)이란 함수 본체 내부를 말한다.
함수 외부에서 변수 값 접근 불가 -> (출력x)
// 함수 내부에서의 변수 선언
function print_Value() {
const value = "Happy!";
}
console.log(value);
아래처럼 함수 내에서만 변수 값 접근 가능 -> (출력o)
function print_Value() {
const value = "Happy!";
console.log(value);
}
print_Value();
var 키워드로 선언된 변수는 오로지 함수의 코드 블록(if, for, while 등)만을 지역 스코프로 인정하는데, 이러한 특성을 함수 레벨 스코프라고 한다.
var x = 1;
if (true) {
var x = 10;
}
console.log(x); //10
아래 코드의 실행 결과를 예측해보자
var x = 1;
function foo() {
var x = 10;
bar();
}
function bar() {
console.log(x);
}
foo();
bar();
위 코드의 실행 결과는 var 함수의 상위 스코프에 따라 정의된다.
⭐ JS 엔진은 함수를 어디서 "호출" 했는지가 아니라, 어디에 "정의" 했는지에 따라서 상위 스코프를 결정하는데, 이를 렉시컬 스코프(lexical scope)라고 한다.
bar
함수가 호출되면 호출된 시점과는 관계없이 정의된 시점에서의 스코프에 의해 결정되므로 자신이 기억하고 있는 전역 스코프를 상위 스코프로 사용한다. 따라서 위 코드를 실행하면 변수 x의 값 1이 두 번 출력된다. var x = "global";
function test() {
var x = "local";
console.log(x); // "local";
}
test();
console.log(x); // "global";
➡️ js 엔진이 스코프 체인을 사용하여 동일한 변수명을 가진 변수를 구별하였기 때문에 다른 결과를 출력할 수 있었던 것이다.
스코프 체인이란?
식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해나가는 것을 말한다.
스코프 체인은 실행 컨텍스트의 렉시컬 환경(LE)을 단방향으로 연결(Chaining)한 것이다.
변수를 참조할 때 스코프 체인을 통해 변수를 참조하는 코드의 스코프에서 시작하여 상위 스코프 방향으로 이동하며 선언된 함수를 검색하는데, 이를 통해 동일한 변수 명이라도 스코프에 따라서 변수를 구별할 수 있는 것이다.
따라서 상위 스코프에서 유효한 변수는 하위 스코프를 참조할 수 없다!!
스코프 참조 자세한 과정
아래 그림이 이해가 안가면 《 호이스팅 》을 보고 오자.
➡️ 이 과정을 스코프 체이닝이라고 한다.
정리
outer는 현재 호출된 함수가 선언될 당시의 LE를 참조(= 그 당시의 환경 정보를 저장)한다.
예를 들어, A함수 내부에 B함수 선언
→ B함수 내부에 C함수 선언(Linked List)
한 경우
결국 타고, 타고 올라가다 보면 전역 컨텍스트의 LE를 참조하게 된다.
항상 outer는 오직 자신이 선언된 시점의 LE를 참조하고 있으므로, 가장 가까운 요소부터 차례대로 접근이 가능하다.
➡️ 결론 : 무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에게만 접근이 가능하다.
예시
var a = 1;
var outer = function() {
var inner = function() {
console.log(a); // 1 : 이 값은 뭐가 나올지 예상해보자
var a = 3;
};
inner();
console.log(a); // 2 : 이 값은 뭐가 나올지 예상해보자
};
outer();
console.log(a); // 3 : 이 값은 뭐가 나올지 예상해보자
var inner = function() {
console.log(a); // 1
var a = 3;
};
inner();
var inner = function() {
var a; // 선언이 호이스팅됨
console.log(a); // 1 : undefined, 초기화는 호이스팅되지 않음
a = 3; // 할당
};
inner();
2 : console.log(a);
는 전역 영역인 var a =1
을 가져오므로 1이 출력된다.
3 : console.log(a);
은 전역 영역 내부 스코프의 var a =1
이 되므로 1이 출력된다.
정리⭐⭐
➡️ 각각의 실행 컨텍스트는 LE 안에 record와 outer를 가지고 있고, outer 안에는 그 실행 컨텍스트가 선언될 당시의 LE정보 (outer 입장에선 전역 context)가 다 들어있으니 scope chain에 의해 상위 컨텍스트의 record를 읽어올 수 있다.