JavaScript Closure
: 클로저는 '어떤 함수에서 선언한 변수를 참조하는 내부 함수를 외부로 전달할 경우, 함수의 실행 컨텍스트가 종료된 후에도 해당 변수가 사라지지 않는 현상'을 의미합니다. 또 그렇게 사용된 함수를 클로저 함수라 합니다. 외부함수가 종료된 이후에도 내부함수가 외부함수에 접근할 수 있습니다. 내부 함수가 소멸될 때까지 외부함수는 소멸되지 않습니다. 렉시컬 스코핑과 함수와의 관계를 잘 봐야합니다.
function makeFunc() {
let name = "Mozilla";
function displayName() {
console.log(name); // "Mozilla"
}
return displayName;
}
var myFunc = makeFunc();
myFunc(); // "Mozilla"
내가 이해한 클로저의 2가지 기준
: 첫번째 기준은 중첩된 함수에서 내부 함수가 상위 스코프의 변수를 참조하고 있어야 합니다. 동일한 코드를 다시 한번 보면 diplsyName() 함수 내부의 name은 선언된 내용이 없어서 상위 함수 makeFunc() 의 name을 참조합니다.
function makeFunc() {
let name = "Mozilla";
function displayName() {
console.log(name);
}
/* return displayName;
}
var myFunc = makeFunc();
myFunc(); */
: 두번째 기준은 외부 함수의 외부로 반환되어 외부 함수보다 더 오래 유지되는 경우로 한정합니다. return을 활용하는 방법도 있지만 콜백으로 전달하는 경우도 포함됩니다.
/* function makeFunc() {
let name = "Mozilla";
function displayName() {
console.log(name);
} */
return displayName;
}
var myFunc = makeFunc();
myFunc();
: 따라서 전체 코드는 아래의 순서대로 실행됩니다.
function makeFunc() {
let name = "Mozilla";
function displayName() {
console.log(name); // "Mozilla"
}
return displayName;
}
var myFunc = makeFunc();
myFunc(); // "Mozilla"
- 9번줄 myFunc는 makeFunc()을 실행한 리턴값 displayName을 변수에 담습니다.
- 11번줄 myFunc()가 실행되면 name은 지역변수로 원래라면 실행시킬 수 없어야 합니다.
- 4번줄 name은 Lexical scoping으로 상위의 변수 name을 참조하게되고 'Mozilla'를 할당합니다.
- makeFunc()함수 선언 당시 환경을 그대로 유지하게 되어 상위변수를 참조하는 것이 유지됩니다.
- 결과값은 name인 "Mozilla"가 반환됩니다. 여기서 displayName은 클로저 함수가 됩니다.
클로저 활용 예제
커링
: 함수 하나가 n개의 함수를 만들어 각각 인자를 순차적으로 받아 호출될 수 있게 체인 형태로 구성한 것입니다. 커링은 마지막 인자가 전달되지 전까지는 원본 함수가 실행되지 않고 대기합니다.
function adder(x) {
return function(y){
return x + y;
}
}
adder(2)(3); // 5
- adder 함수를 호출하면서 순서대로 2, 3이 각각 x와 y로 할당됨
: 화살표 함수를 사용할 경우 커링 함수의 흐름이 한눈에 파악됩니다.
let maxValue = function (func) {
return function(a){
return function(b){
return function(c){
return function(d){
return function(e){
return func(a, b, c, d, e);
}
}
}
}
}
}
let getMax = maxValue(Math.max);
console.log(getMax(1)(2)(3)(4)(5)); // 5
let maxValue = func => a => b => c => d => e => func(a, b, c, d, e);
let getMax = maxValue(Math.max);
console.log(getMax(1)(2)(3)(4)(5)); // 5
- ㅇ
: x의 값을 고정해 놓고 재사용할 수 있습니다. 커링을 변수에 할당하고 다시 그 변수를 인자를 넣어 호출하면 됩니다. 아래에는 동일한 내용을 화살표 함수를 이용해서 나타냈습니다.
function adder(x) {
return function(y){
return x + y;
}
}
let add100 = adder(100);
add100(2); // 102
adder => x => y => x + y;
let add100 = adder(100);
add100(2); // 102
: 외부 함수의 변수를 내부함수가 템플릿 함수처럼 재사용 할 수도 있습니다.
function htmlMaker(tag) { // 1번 여기로
let startTag = '<' + tag + '>';
let endTag = '</' + tag + '>';
return function(content) { // 2번 여기로
return startTag + content + endTag;
}
}
let divMaker = htmlMaker('div'); // 1번
divMaker('안녕하세요') // 2번
// <div>안녕하세요</div>
- htmlMaker 함수를 통해 div가 고정 재사용되고, divMaker를 호출시 인자가 content로 function에 할당됨
클로저 모듈 패턴
: 변수를 스코프 안쪽에 가두어 함수 밖으로 노출시키지 않고 모듈 패턴처럼 사용할 수 있습니다. 아래 예시의 return은 객체의 형태를 띄고 있습니다.
function makeCounter() {
let privateCounter = 0;
return {
increment: function() {
privateCounter ++
},
decrement: function() {
privateCounter --
},
getValue: function() {
return privateCounter;
}
}
}
let counter1 = makeCounter();
counter1.increment()
counter1.increment() // 2
let counter2 = makeCounter();
counter2.increment()
counter2.increment()
counter2.increment()
counter2.increment()
counter2.increment() // 5
counter1.getValue // 2
counter2.getValue // 5
- 만약 privateCounter가 임의조작 가능하면 연산 결과가 달라질 수 있음.
- makeCounter 함수 내에 return에 함수를 두어 increment나 decrement, getValue에 접근하지 못하게 함.
- let count2 = makeCounter (); 형태로 새로운 변수에 할당하면 기존 함수와 별도의 새로운 함수로 재사용 가능.
- 함수 내에 let privateCounter 라는 지역 변수가 있기 때문에 두 변수의 연산결과가 달라지게 됨.
정리
- closure는 어떤 함수에서 선언한 변수를 참조하는 함수의 실행 컨텍스트가 종료된 후에도 해당 변수가 사라지지 않는 현상
- 중첩된 함수에서 내부 함수가 상위 스코프의 변수를 참조하고 있어야 함.
- 외부 함수보다 더 오래 유지되는 경우로 한정함.
- 커링. n개의 함수를 하나의 함수가 받아 호출
- 클로저 모듈 패턴. 스코프 안쪽에 객체 형태로 return
참고
'웹 > JavaScript' 카테고리의 다른 글
JavaScript 내장 고차 함수(forEach, filter, map, reduce) (0) | 2021.01.24 |
---|---|
JavaScript 일급객체와 고차 함수 (0) | 2021.01.24 |
JavaScript 실행 컨텍스트(execution context)와 호이스팅(hoisting) (0) | 2021.01.10 |
JavaScript Scope chain과 Lexical scoping (0) | 2021.01.10 |
JavaScript Scope (0) | 2021.01.10 |