[Javascript] 클로저(Closure)란?
JavaScript

[Javascript] 클로저(Closure)란?

728x90

클로저 정의

클로저(Closure)는 자유변수에 접근할 수 있는 내부함수 또는 그 환경을 포함하는 코드를 지칭한다.

 

MDN(Mozilla Developer Network) [모질라 재단및 다른 IT기업들이 사용하는 웹 개발을 위한 문서 저장소이자 수많은 프로그래밍 입문자들을 위한 학습 장소] 에서는 다음과 같이 정의한다.

“A closure is the combination of a function and the lexical environment within which that function was declared.”
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.

응? 자유변수가 무엇인가? 렉시컬 환경은 또 무엇인가?

 

분명 정의는 됐는데 알아들을 수가 없다.

 

이 문장을 이해하기 위해 일단 자유변수부터 차근차근 알아보자.

 

변수의 범위(Scope)와 수명(Lifetime)

자바스크립트 변수들은 지역(local) 또는 전역(global)의 변수 범위를 갖는다

// 지역변수
function printName(){
    var name = "Jae";
    console.log(name);
}

// 전역변수
var name = "Jae";
function printName(){
    console.log(name);
}

function printName(){
    name = "Jae"; // 전역변수가 됨
    console.log(name);
}

위 예시처럼

정해진 함수내에서만 사용할 수 있는 변수가 지역변수이고,

윈도우(window) 객체에 속하여 페이지 내 모든 스크립트에서 사용되어질 수 있는것이 전역변수이다.

주의: var, let, const 키워드를 사용하지않고 함수내에 선언이 되어도 자동으로 전역변수가 되어버린다. 고로 조심하자.

이것도 웬만해서 모두가 알고있겠지만,

지역변수의 수명은 짧다. 함수가 호출될 때 함수 안에서 생성되고, 함수가 종료될 때 지워진다.

전역변수는 애플리케이션(윈도우 또는 웹페이지)가 살아 있는 동안 살아있다.

 

중첩 함수(Nested Function) 및 함수 반환

자바스크립트는 일급 함수를 지원하므로

- 함수를 변수에 저장

- 파라미터로 함수를 넘기기

- 함수를 반환

이 가능하다!

function makePrinter() {
    var outer_name = "재횬";

    function printName() {
        var inner_name = "다욘";
        console.log(outer_name);
        console.log(inner_name);
    }

    return printName;
}
var print = makePrinter();

 

클로저 - 자유변수 떡밥 회수

그럼 이제 다음 코드를 실행하면 어떻게 되는지 보자

function makePrinter() {
    var outer_name = "재횬";
    function printName() {
        var inner_name = "다욘";
        console.log(outer_name);
        console.log(inner_name);
    }

    return printName;
}
var print = makePrinter();
print();
//출력
재횬
다욘

분명 outer_name 변수와 inner_name 변수는 함수내부에서 선언된 지역변수이다.

지역변수의 수명은? 답: 함수가 종료되는 시점에서 사라진다.

하지만 makePrinter() 호출 후에 print() 호출에도 살아서 출력을 해준다는 것!

클로저는 자신을 포함하고 있는 외부 함수의 인자, 지역변수 등을 외부 함수가 종료된 후에도 사용할 수 있다.

이러한 변수를 자유변수(free variable)라고 부른다.

 

그리고 이러한 자유변수를 가지는 코드를 클로저라고 한다.

 

렉시컬 스코핑(lexical scoping)

클로저는 어휘적 범위 지정(lexical scoping)의 한 예이다.

"lexical"이란, 어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다.

 

스코프는 함수를 호출할 때가 아니라 함수를 어디에 선언하였는지에 따라 결정된다. 이를 렉시컬 스코핑(Lexical scoping)라 한다.

 

클로저를 어디에 쓸까?

처음 이 개념을 접하면서 가장 먼저든 생각은 '도대체 이걸 어디다가 쓰지?' 였다.

 

다시하면 클로저의 가장 중요한 포인트를 생각해보면 자유변수를 갖는 것이다.

자유변수는 내부변수를 외부로 끌어내는 것 같으면서도 내부변수에 직접적으로 접근할 수가 없다.

// 카운터를 위한 클로저를 생성 
var counter = (function() { 
  // private 변수 입니다.(외부 접근 불가) 
  var privateCounter = 0; 

  // private 함수 입니다.(외부 접근 불가) 
  function changeCounter(val) { 
    privateCounter += val; 
  } 

  // public 함수를 가지는 객체를 반환
  return { 
    // 증가 기능을 가지는 public 함수
    inc: function() { 
        changeCounter(1); 
    }, 
    // 감소 기능을 가지는 public 함수
    dec: function() { 
        changeCounter(-1); 
    }, 
    // public 함수 입니다.(현재값 조회) 
    val: function() { 
        return privateCounter; 
    } 
  }; 
})(); 

counter.inc(); 
counter.inc(); 
console.log("after increment : " + counter.val()); 
counter.dec(); 
console.log("after decrement : " + counter.val()); 


결과) 
after increment : 2 
after decrement : 1

var counter = (function() {})();
위와같은 형식으로 자기호출 함수로 하나만 생성을 했다.

 

1. 카운터 값을 유지하는 변수는 자유 변수로 카운터 밖에서는 직접 접근할 수 없다.

2. 자기 호출 함수는 호출시 최초 한번만 실행되므로 var counter가 생겨남과 동시에 0으로 초기화된다.

3. 클로저 내부에 정의된 카운터 값을 변경하는 changeCounter() 함수는 외부에서는 접근할 수 없는 private 함수가 된다.

 

이처럼 클로저를 통해 객체지향언어의 객체가 가지는 private 멤버 변수, private 메소드, public 메소드와 유사한 기능을 구현할 수 있다.

 


출처: 

https://offbyone.tistory.com/135 [쉬고 싶은 개발자]

https://hyunseob.github.io/2016/08/30/javascript-closure/ [DailyEngineering]

728x90