티스토리 뷰

JavaScript

[자바스크립트] 클로저(Closure)

버미노트 2016.12.22 18:21


JS


클러저(Closure)



1. 클로저(Closure)란?

MDN(https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures) 에서는 아래와 같이 정의하고 있습니다.


 클로저는 독립적인 (자유) 변수를 가르키는 함수이다. 또는, 클로저 안에 정의된 함수는 만들어진 환경을 '기억한다'


위의 말로는 이야기 되지 않습니다. 그래서 영문도 살펴 보았습니다.


Closures are functions that refer to independent (free) variables (variables that are used locally, but defined in an enclosing scope).

In other words, these functions 'remember' the environment in which they were created.


허허.. 영어가 짧아, 영문으로 보아도 잘 이해가 되지 않습니다.


MDN을 찬찬히 읽어 보다가 좀 더 이해하기 쉬운 한 구절을 찾았습니다.


클로저는 두개의 것(함수, 그 함수가 만들어진 환경)으로 이루어진 특별한 객체의 한 종류이다.


클로저 = 함수 + 함수를 둘러싼 환경(Lexical environment) 입니다. ([JavaScript] 유효범위(Scope)와 호이스팅(Hoisting) 참고)

function outer() {
    var outerText = "Is Closure ?";
    function inner() {
        console.log(outerText);
    }
    inner();
}
outer();

- 코드 1 -


코드 1 실행 결과코드 1 실행 결과


코드 1의 결과는 당연히 Is Closure ? 이 출력이 됩니다. 그렇다면 inner 함수를 클로저라고 할까요? 아닙니다.

inner 함수는 스코프 체인([JavaScript] 유효범위(Scope)와 호이스팅(Hoisting) 참고)을 통해 outerText를 출력하는 내부 함수 일뿐 클로저가 아닙니다. inner 함수는 outer 안에서만 사용할 수 있는 내부 함수 입니다.


진짜 closure 예를 들어 보겠습니다.

function outer() {
    var outerText = "Is Closure !";
    function inner() {
        console.log(outerText);
    }
    return inner;
}
var myClosure = outer();
myClosure();

- 코드 2 -


코드 2 실행 결과코드 2 실행 결과


outer 함수의 리턴 값은 내부함수 inner 함수 입니다. outer 함수가 종료되면, 지역변수는 없어지는 것이 상식적입니다. 하지만 코드 2의 결과는 정상적으로 출력되는 것을 보실 수 있습니다. 이유는 바로 myClosure 함수가 클로저이기 때문입니다.


앞에서 말씀드렸던 것 처럼, MDN에서 설명한 클로저를 는 밑과 같습니다.


클로저는 두개의 것(함수, 그 함수가 만들어진 환경)으로 이루어진 특별한 객체의 한 종류이다. 환경이라 함은 클로저가 생성 될 때

그 범위 안에 있던 여러 지역 변수들로 이루어진다.


때문에 myClosure는 inner 함수와 "Is Closure !" 문자열을 포함하는 클로저입니다.



2. 반복문 안에서 클로저 만들기

반복문 안에 클로저를 사용하는 경우 예상과 다른 동작을 하는 경우가 있어 주의가 필요합니다.

function count() {
    for (var i = 1; i < 10; i++) {
        setTimeout(function () {
            console.log(i);
        }, i*100);
    }
}
count();

- 코드 3 -


코드 3의 원하는 출력 값은 1부터 9까지 0.1초마다 호출 되는 것입니다. 하지만 결과는 밑의 그림과 같이 10이 9개가 출력됩니다.


코드 4 실행 결과코드 4 실행 결과


이유는 바로 클로저 때문입니다. count 함수가 종료 되고, console.log로 i값을 출력하게 되는데, 하나의 환경(다른 말로 하나의 Lexical environment)을 공유하기 때문에, count가 종료 될 때의 i값인 10을 9번 출력하게 되는 것입니다.


원인이 하나의 환경을 공유하기 때문에 발생하는 문제라면, 하나의 환경을 공유하지 않게 하면 문제는 해결 됩니다. 문제를 해결하기 위해 두가지 방법이 있습니다.


새로운 스코프를 추가하여 반복 할 때마다 그 스코프에 각각 따로 저장하는 방법

 function count() {
     var i;
     for (i = 1; i < 10; i += 1) {
         (function(countingNumber) {
             setTimeout(function () {
                 console.log(countingNumber);
             }, i * 100);
         })(i);
     }
 }
 count();

즉시 실행 함수를 사용하여, setTimeout으로 호출된 각각의 즉시 실행 함수의 환경(Lexical environment)을 가지게 하는 방법입니다.


ES6에서 추가된 블록 스코프를 사용하는 방법

function count() {
    for (let i = 1; i < 10; i += 1) {
        setTimeout(function () {
            console.log(i);
        }, i * 100);
    }
 }
 count();

let 키워드로 블록 스코프에서 존재하는 변수를 만드는 방법입니다. ([JavaScript] 유효범위(Scope)와 호이스팅(Hoisting) 참고)




(참조 http://meetup.toast.com/posts/86)

댓글
댓글쓰기 폼