본문 바로가기

Programing/JavaScript

[JavaScript] Closure

Closure

· 외부함수의 변수에 접근할 수 있는 내부 함수

· scope chain으로 표현되기도 한다

· 보통 함수를 return 하여 사용! 

· return하는 내부 함수를 closure 함수라고 지칭




Question

각각의 함수 호출은 어떠한 결과를 콘솔에 출력할까요?


1
2
3
4
5
6
7
8
9
10
11
12
function outer() {
  console.log('outer fn invoked');
  function inner() {
    console.log('inner fn invoked');
  }
  return inner;
}
 
outer(); // ?
outer()();  // ?
var innerFn = outer(); // ?
innerFn(); // ?
cs


outer(); // 'outer fn invoked' , 함수(inner)가 리턴됨 

outer()();  // 'outer fn invoked'   'inner fn invoked' 바깥 함수가 실행되고 안의 함수도 실행됨

var innerFn = outer(); // 'outer fn invoked'

innerFn(); // outer()();와 같음




Closure

1
2
3
4
5
6
7
8
function outer() {
  var outerVar = 'outer fn variable ';
  function inner() {
    var innerVar = 'inner fn variable ';
    console.log(outerVar + innerVar);
    // 외부 함수의 변수(outerVar)를 내부 함수, 즉 closure가 사용할 수 있음
  }
}
cs




Closure가 가지는 세가지 scope chain

1. closure 자신에 대한 접근(closure function 내에 정의된 변수)

2. 외부 함수의 변수에 대한 접근

3. 전역 변수에 대한 접근



1
2
3
4
5
6
7
8
9
10
11
12
13
var greeting = 'Hello'// 전역 변수에 대한 접근 가능
function showName(firstName, lastName) {
  var nameIntro = 'My name is ';
  function makeFullName() {
    // 이 내부 함수(makeFullName)는 외부 함수(showName)의 변수뿐만 아니라
    // 파라미터 까지 사용할 수 있다.
    return greeting + ', ' + nameIntro + firstName + ' ' + lastName;
  }
  return makeFullName();
}
 
showName('Michael''Jackson');
// Hello, My name is Michael Jackson
cs




유용한 Closure 예제

currying (Functional Programming)

함수 하나가 n개의 인자를 받는 대신, n개의 함수를 만들어 각각 인자를 받게 하는 방법


· without currying


1
2
3
4
function add(x, y) {
  return x + y;
}
add(23); // 5
cs


· with currying


1
2
3
4
5
6
function adder(x) {
  return function(y) {
    return x + y;
  }
}
adder(2)(3); // 5
cs


무엇이 좋은가?

· closure가 저장된 parameter를 사용하므로, template 함수를 만들고자 할 때 유용


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function elementMaker(tagName) {
  var startTag = '<' + tagName + '>';
  var endTag = '</' + tagName + '>';
  return function(content) {   // closer 함수
    return startTag + content + endTag;
  }
}
 
elementMaker('div')('hello world'); // <div>hello world</div>
var divMaker = elementMaker('div');
divMaker('code states'); // <div>code states</div>
divMaker('great'); // <div>great</div>
 
var h1Maker = elementMaker('h1');
h1Maker('Headline'); // <h1>Headline</h1>
cs




Closure Module Pattern

Simple counter 예제


· 변수를 scope 안쪽에 감추어 함수 밖에서 노출시키지 않을 수 있음


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function makeCounter() {
  var privateCounter = 0;
 
  function changeBy(val) {
    privateCounter += val;
  }
 
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    getValue: function() {
      return privateCounter;
    }
  }
}
 
var counter1 = makeCounter();   // { increment : f, decrement : f, getValue: f }
counter1.increment();    // 내부적으로 privateCounter +1
counter1.increment();    // 내부적으로 privateCounter +1
counter1.getValue();    // 2
 
var counter2 = makeCounter();
counter2.increment();    // privateCounter +1
counter2.decrement();    // privateCounter +1
counter2.increment();    // 2 (독립적으로 값이 나온다)
cs