: 정보를 저장하기 위한 메모리 공간 + 코드 실행 순서 관리
-> (lexical environment + stack)
각각의 코드들을 실행할 때 나오는 데이터를 관리하기 위해 실행 컨텍스트 내에 저장한다.
여기서의 메모리 공간이 lexical environment이다.
코드 실행 순서 관리는 stack으로 관리된다.(Execution context Stack)
: 현재 실행 컨테스트에 대해서 현재 스코프, 상위 스코프에 대한 참조를 가지고 있다.
: 코드 실행 순서를 저장한다.
const x = 1;
function foo() {
const y = 2;
function bar() {
const z = 3;
console.log(x + y + z);
}
bar();
}
foo();
: 4가지 종류의 소스 코드가 있다.
코드 종류에 따라 scope가 달라지게 된다. 각각의 코드가 실행 될 때마다 별도로 실행 컨텍스트가 생성되고 관리된다.
전역 코드
: 전역에 존재하는 소스 코드
전역 함수의 내부 코드는 포함되지 않는다.
함수 코드
: 함수 내부에 존재하는 소스 코드
중첩 합수의 내부 코드는 포함되지 않는다. 별도의 함수 코드로 간주한다.
eval 코드 (중요X)
모듈 코드 (중요X)
var obj = [] // 전역 코드
function myFunc() { // 전역 함수
console.log('Hello'); // myFunc의 함수 코드
// nested function, inner function, 중첩 함수
function sayHello() {
console.log('하이'); // sayHello의 함수 코드
}
}
myFunc(); // 전역 코드
전역 변수를 관리하기 위해 전역 scope를 생성한다. -> 전역에 var keyword로 선언된 식별자를 찾고, window 객체를 만들고, 그 식별자를 window 객체에 binding 한다.
=> 이런 작업을 하기 위해 전역 실행 컨텍스트를 생성한다.
함수 내부에서 사용하는 지역 변수, 매개 변수, arguments 관리하기 위해 지역 scope를 생성한다. -> 이 지역 scope를 전역 scope와 연결해서 scope chain을 생성한다.
=> 이런 작업을 하기 위해 함수 실행 컨텍스트를 생성한다.
: 함수와 그 함수가 선언된 lexical 환경의 조합(MSN 정의)
JavaScript 고유의 개념이 아니다. 함수형 언어들이 가지고 있는 특징이다. 일급객체에 대한 이해가 필요하다.
Closure는 중첩함수이다.
이 중첩함수가 외부함수의 결과값으로 return 된다.
return되는 중첩함수가 외부함수의 식별자를 참조한다.
return되는 중첩함수의 life cycle(생명주기)가 외부함수보다 길어야 한다.
이때, 중첩함수에서 외부함수에 대한 참조가 남아있기 때문에 외부함수의 실행은 execution context stack에서 제거 되지만 외부함수의 lexical 환경은 메모리에 남아있어서 중첩함수에 의해 사용될 수 있는 현상
closure 예시
const x = 1;
function outer() {
const x = 10;
const inner = function() {
console.log(x);
}
return inner; // 1급 객체라서 함수를 리턴할 수 있다.
}
const innerFunc = outer(); // 여기서 함수가 끝나면 지역변수 x = 10이 사라졌어야 하는데 남아있다.
innerFunc();
아래 코드는 closure가 아니다. x,y를 참조하지 않기 때문이다.
function foo() {
const x = 1;
const y = 2;
function bar() {
const z = 3; // foo의 식별자를 참조하고 있지 않으므로 closure가 아니다.
console.log(z);
}
return bar;
}
const bar = foo();
bar();
아래 코드도 closure가 아니다. 중첩함수를 리턴하지도 않고 있고, 중첩함수가 외부함수보다 생명주기도 짧기 때문이다.
function foo() {
const x = 1;
const y = 2;
function bar() {
console.log(x);
}
bar();
}
foo();
let num = 0;
const increase = function() {
return ++num;
}
console.log(increase());
console.log(increase());
console.log(increase());
실행 결과 : 1
2
3
문제 : num을 전역이기 때문에 아무 곳에서나 변경할 수 있다. 데이터를 보호할 수 있는 방법이 없다.
const increase = function() {
let num = 0;
return ++num;
}
console.log(increase());
console.log(increase());
console.log(increase());
실행 결과 : 1
1
1
문제 : num이 함수를 실행할 때마다 초기화 된다.
const increase = (function() {
let num = 0; // num을 지역변수화 해서 보호한다.
return function() {
return ++num; // 외부함수의 변수를 참조하기 때문에 closure이다.
}
}());
console.log(increase());
console.log(increase());
console.log(increase());
실행 결과 : 1
2
3
closure가 될 때 함수를 직접 리턴하지 않고, 객체에 담아서 리턴해도 된다.
const counter = (function() {
let num = 0; // 외부로부터 보호해야 할 변수
return { // 함수를 직접 리턴하지 않고, 객체에 담아서 리턴한다.
increase() {
return ++num;
},
decrease() {
return --num;
}
}
}());
console.log(counter.increase());
console.log(counter.increase());
console.log(counter.increase());
console.log(counter.decrease());
console.log(counter.decrease());
console.log(counter.decrease());
실행 결과 : 1
2
3
2
1
0