
스코프
- 변수와 함수, 클래스 이름과 같은 식별자가 본인이 선언된 위치에 따라서 자신이 참조 가능 여부가 결정되는 것 → 스코프의 속성이라고 한다면 정적 스코프와 동적 스코프
- 자바스크립트에서 어떤 함수가 있을 때 그 함수의 유효 범위는 그 함수가 어디서 실행했느냐가 아니라 어디서 정의됐느냐에 따라 달라짐 → 정적 스코프(렉시컬 스코프)
- 스코프의 범위는 선언되는 위치에 따라 전역 스코프, 지역 스코프
스코프 체인
스코프 체인 예시
let x = '나는 전역 x';
function outer() {
let y = '나는 outer함수의 지역 y';
**console.log(x);
console.log(y);
function inner() {
let x = '나는 inner함수의 지역 x';
**console.log(x);
console.log(y);
}
inner();
}
outer();
console.log(x);
console.log(y);
- 함수는 전역에서 선언될 수 있지만, 함수의 내부에서도 함수가 선언될 수 있음
- 함수 내부에서 정의된 함수를 중첩 함수 혹은 내부 함수라고 하고, 중첩 함수를 포함하는 함수는 외부 함수
- 즉, 스코프 또한 중첩이 될 수 있고 스코프가 계층적인 구조를 가질 수 있다는 것을 의미 → 스코프 체인
- 변수를 참조할 때 자바스크립트는 스코프 체인을 통해 변수를 참조 → 최상위 전역 스코프까지 확인했을 때 해당 변수가 없을 때에는
reference error
를 출력
- 작업 시 하위 스코프에서 상위 스코프 변수를 참조할 수 있는 이유가 스코프 체인의 단방향성 때문
스코프 레벨
- 블록 레벨 스코프 - if문, for문, 함수 등
- 함수 레벨 스코프 - only 함수
- 상위 스코프를 결정 짓는 요소
- 동적 스코프 → 함수가 호출되는 시점에 결정 → 런타임 도중에 실행 컨텍스트에 의해서 스코프가 결정
- 정적 스코프 → 함수가 선언되는 시점에 결정 → 렉시컬 스코프 → 자바스크립트는 렉시컬 스코프를 따르기 때문에 선언되는 시점에 상위 스코프가 결정, 함수 본인의 내부 슬롯에 상위 스코프에 대한 참조를 저장
- 함수 호출 → 실행 컨텍스트 생성 → 실행 컨텍스트 스택에 푸쉬 → 렉시컬 환경 생성 → 코드의 실행이 끝나면 실행 컨텍스트 스택 제거
클로저
- 중첩 함수가 상위 스코프의 식별자를 참조하고 있고, 본인의 외부 함수보다 더 생명주기가 길다면 이 중첩 함수는 클로저
- 외부 함수가 내부 함수보다 먼저 생명주기가 끝나더라도 내부 함수가 외부 함수의 변수를 참조하는 것
- 가비지 컬렉터와도 관련 → 가비지 컬렉터는 어떤 값을 참조하는 변수가 하나라도 있다면 그 값은 수집 대상에 포함되지 않음, 외부 함수의 실행이 종료되더라도 내부 함수를 실행함으로써 호출될 가능성이 열린 것 → 즉, 내부 함수의 실행 컨텍스트가 활성화되면 외부 함수의 렉시컬 환경이 필요할 것이기 때문에 수집 대상에서 제외
- closure라는 이름처럼 state를 안전하게 은닉하고, 특정 함수에게만 state 변경을 허용하기 위해 사용한다고 볼 수 있음
- 클로저를 활용한 은닉화 사례
function makeCounter() {
let num = 0;
return function () {
return num++;
}
}
let counter = makeCounter();
console.log(counter());
console.log(counter());
console.log(counter());
- 덧셈이라고 하는 함수를 더하기함수공장 안에서 호출한 것이 아닌데도 더하기함수공장의 1이라고 하는 값을 내부적으로 스코프로 간직하고 있으니 접근 가능하다는 문법적 요소
function 더하기함수공장(초기값) {
function 덧셈(숫자) {
return 초기값 + 숫자;
}
return 덧셈;
}
let 더하기1 = 더하기함수공장(1);
console.log(더하기1(1));
console.log(더하기1(2));
let 더하기2 = 더하기함수공장(2);
console.log(더하기2(1));
console.log(더하기2(2));
참고자료