:자바스크립트를 포함한 모든 프로그래밍 언어에서 가장 기본적이고 중요한 개념 중 하나로 변수 이름, 함수 이름, 클래스 이름과 같은 식별자가 본인이 선언된 위치에 따라 다른 코드에서 자신이 참조될 수 있을지 없을지 결정되는 것을 의미함
예시 코드
function add(x,y) {
console.log(x, y); // 3 6
return x+y;
}
add(3,6);
console.log(x,y); // error
➡️ 자신이 선언된 위치에 따라 유효 범위가 결정된 것을 확인할 수 있음
함수는 위의 이미지처럼 중첩될 수 있는데 순서대로 전역함수 - 외부함수 - 중첩함수가 있음
함수가 중첩된다
➡️ 각 함수의 지역 스코프도 중첩
➡️ 중첩에 의해 계층적 구조를 가질 수 있다
아래의 예를 통해 다시 확인하자
var a = "나는 글로벌";
function outer() {
var b = "나는 outer의 지역변수";
console.log(a); // 나는 글로벌
console.log(b); // 나는 outer의 지역변수
function inner() {
var a = "나는 inner의 지역변수";
console.log(a); // 나는 inner의 지역변수
console.log(b); // 나는 outer의 지역변수
}
inner();
}
outer();
console.log(a); // 나는 글로벌
console.log(b); // err
어떻게 이런 결과가 나왔을까?
전역 스코프
x:나는 글로벌, outer: func obj
👆 : outer에도 없으면 하나 위로
outer의 지역 스코프
y: 나는 outer의 지역변수, inner: func obj
👆: inner에 변수가 없으면 하나 위로
inner의 지역 스코프
x: 나는 inner의 지역변수
➡️ 전역에도 없다! ➡️ 레퍼런스 에러
함수 호출 ➡️ 실행 컨텍스트 생성 ➡️ 렉시컬 환경 생성 ➡️ 실행 ➡️ 실행 컨텍스트에서 pop() 제거
(렉시컬 환경: 어떤 코드가 어디서 실행되고 본인 주변에 어떤 코드들이 있는지 대체적인 정보를 담고 있는 환경)
const x = 1;
function outer() {
const x = 10;
const inner = () => {
console.log(x);
};
return inner;
}
const guess = outer();
guess(); // 10
위의 코드를 보면 콘솔에 10이 찍히는걸 확인할 수 있는데 아까 함수가 실행 완료되면 실행 컨텍스트에서 pop된다고 하지 않았나??
그럼 어떻게 10이 출력된걸까??
이것을 이해하기 위해 필요한 개념이 클로저이다
: 함수와 함수가 선언된 어휘적 환경의 조합으로 하나의 state가 의도치 않게 변경되지 않도록 state를 안전하게 은닉하고 특정 함수에게만 state변경을 허락하게 해줌
위의 예처럼 외부함수의 변수를 참조하고 외부함수보다 더 오래 유지된다면 이런 중첩함수를 클로저라고한다!
외부함수: outer
중첩함수: inner
참조변수(자유변수): x
outer 함수는 실행컨텍스트 스택에서 완전히 제거가 되었지만 렉시컬 환경에서까지 제거되진 않음
guess는 inner 함수 객체를 참조하고 있고 inner 객체는 본인의 내부 슬롯에 저장된 outer 함수의 렉시컬 환경을 참조 따라서 삭제 대상이 되지 않음
➡️ 따라서 변수 x 다시 참조 가능!
생명을 마감하고 실행 컨텍스트에사 사라졌지만 내부 슬롯에 저장된 상위 스코프에 의존하여 상위 스코프 내 식별자 참조 가능하게 된 것!