함수와 클로저(closure)
스코프
함수 선언식
으로 만들어진 함수는함수 레벨 스코프
를 갖는다.var
또한함수 레벨 스코프
- let, const 는
블록 레벨 스코프
// 함수 선언문 -> 함수 호이스팅 function foo(){ } // 함수 표현 식 -> 변수 호이스팅 const foo = function() { } function foo() { if(true) { var color = 'blue'; } console.log(color); // blue } console.log(color); // color is not defined foo(); function bar() { if(true) { let color = 'blue'; } console.log(color); // Reference error : color is not defined } bar()
- let, const 는
var
은let
과const
로 대체가 가능하며,var
자체의 스코프가 다르기 때문에 혼란을 야기하여var
을 사용하지 않는다.
- 스코프는 함수를 실행 할 때가 아닌 선언 할 때 생긴다.
함수 vs 클로저(Closure)
- 함수 + 함수를 둘러싼 환경(Lexical environment) : 렉시컬 스코프
- 함수가 생성되는 시점에 생성 → 생성될 때 그 함수의 렉시컬 환경을 포섭(closure)
- 자바스크립트의 모든 함수는 상위 스코프(global 포함) 을 기억하므로 함수 + 렉시컬 환경 의 조합이 성립하고, 이론적으로는 모든 함수는 클로저 이지만 일반적으로 모든 함수를 클로저 라고 부르지는 않는다.
- 상위 스코프의 어떠한 식별자도 참조하지 않는 함수는 클로저가 아니다
- 대부분의 모던 브라우저는 상위 스코프의 어떤 식별자도 참조하지 않는 경우 상위 스코프를 기억하지 않는다.
- 상위 스코프가 Block ?
function foo() {
var color = 'blue';
function bar() {
console.log(color);
}
bar();
}
foo();
- bar() : foo() 안에 속하기 때문에 foo 스코프를 외부 스코프로 참조
- bar는 자신의 렉시컬 스코프 체인을 통해 foo 의 color를 참조
- bar 는 클로저인가?
- bar 는 foo 안에서 정의되고 실행 되었을 뿐, foo 밖으로 나오지 않았기 때문에 클로저라고 부르지 않는다.
var color = "red";
function foo() {
var color = "blue"; // 2
function bar() {
console.log(color); // 1
}
return bar;
}
var baz = foo(); // 3
baz(); // 4
- bar 는 color 를 찾아 출력하는 함수로 선언
- bar 는 외부 환경 참조로 foo 의 환경을 외부 스코프로 참조
- bar 를 global 의 baz 란 이름으로 데려옴 → return bar
- global 에서 baz(=bar)를 호출
- bar는 자신의 스코프에서 color 를 찾는다
- 없기 때문에 외부 환경 참조를 찾아간다 (foo 의 환경)
- foo 환경에서 var color = "blue" 를 찾았으므로, blue 를 출력한다.
- bar 는 자신이 생성된 렉시컬 스코프에서 벗어나 global 환경에서 baz 라는 이름으로 호출되어 스코프 탐색으로 현재 실행 스택과 관련 없는 foo 를 거쳐갔다.
- bar 의 생성과 직접적인 관련이 없는 global 환경 에서 호출 해도 bar 의 외부 참조환경인 foo 에서 color 를 찾음
- Javascript 의 스코프는 렉시컬 스코프, 이름의 범위는 소스코드가 작성된 그 문맥에서 바로 결정되는 것
- foo 의 렉시컬 환경은 foo(); 수행이 끝난 후 GC 가 회수?
- bar 가 foo 의 렉시컬 환경을 참조하고 있기 때문에 회수하지 않는다.
- foo 함수의 실행 컨텍스트는 스택에서 제거된다.
- foo 의 렉시컬 환경은 foo(); 수행이 끝난 후 GC 가 회수?
클로저 선언이나 축약 표현
const addFunc = function(a) {
return function(b) {
return a+b;
}
}
const add3 = addFunc(3)
add3(5) // return 8
// add3 은 아래처럼 작동
const add3 = function() {
const a = 3
return function(b) {
return a+b
}
}
// 함수를 실행할 때 (addFunc3) 새로운 렉시컬 환경이 만들어짐
// 해당 환경엔 매개변수(a)와 함수의 지역함수가 저장됨
// 매개변수(3) 이 저장된 것과 함수의 지역함수(const a=3) 이 저장된 것이 같다고 할 수 있음
축약
const addFunc = a => b => a+b
const add3 = addFunc(3)
add3(5) // return 8
// Currying
addFunc(2)(8) // return 10
참고