본문으로 바로가기

함수 유효범위와 클로저(Closure)

클로저는 외부함수(포함하고 있는)의 변수에 접근할 수 있는 내부 함수를 말합니다.

스코프 체인(scope chain)으로 표현되기도 합니다. 


클로저는 세가지 스코프 체인을 가지고 있습니다.

클로저 자신에 대한 접근(자신의 블럭내에 정의된 변수), 외부 함수의 변수에 대한 접근, 그리고 전역 변수에 대한 접근. 이렇게 3단계로 구분할 수 있습니다. 

내부 함수는 외부 함수의 변수뿐만 아니라 파라미터에도 접근할 수 있습니다. 

단, 내부 함수는 외부 함수의 arguments 객체를 호출할 수는 없습니다. (하지만, 외부 함수의 파라미터는 직접 호출할 수 있습니다.)




함수 유효범위와 클로저

자바스크립트 인터프리터가 함수를 호출하게 되면 인터프리터는 호출 객체(활성화 객체로도 알려진)를 생성하게 됩니다. 

모든 함수 매개변수 및 var 문으로 선언한 지역 변수는 호출 객체 안에 정의되고 호출 객체의 프로퍼티로 정의됩니다. 


다음 예제의 경우, f1 호출 객체에는 y1과 x1 프로퍼티가 담기고, f2 호출 객체에는 y2와 x2 프로퍼티가 담깁니다.

javascript
function f1(y1) {
    var x1 = 1;
    function f2(y2) {
        var x2 = 2;
    }
    f2();
}
f1(); 



함수 유효범위 체인

자바스크립트 함수는 그것이 실행되고 있는 유효범위가 아니라 그것이 정의된 유효범위 내에서 실행하게 됩니다. 

함수를 정의하면 현재 유효범위 체인이 저장되고 해당 함수의 내부 상태의 일부를 구성하게 됩니다. 

가장 높은 레벨의 유효범위 체인은 단순히 전역 객체로만 구성되어 있습니다. 

중첩 함수를 정의하면 유효범위 체인이 바깥 함수를 포함하게 되어 중첩된 함수에서는 바깥 함수의 인자와 지역 변수에 모두 접근할 수 있게 됩니다. 

자바스크립트 인터프리터가 함수를 호출하면 해당 함수의 호출 객체는 유효범위 체인 앞에 추가됩니다. 


다음 예제의 경우 f2가 호출되면 유효범위 체인에는 세 개의 객체가 포함되는데, 이러한 객체로는 해당 함수 자체의 호출 객체와 f1의 호출 객체, 그리고 전역 객체가 있습니다.

javascript
function f1(y1) {
    var x1 = 1;
    function f2(y2) {
        var x2 = 2;
    }
    f2();
}
f1(); 



자바스크립트 클로저

자바스크립트 함수는 실행될 코드와 코드가 실행되는 유효범위의 조합에 해당됩니다. 

이러한 코드와 유효범위의 조합은 컴퓨터 과학 분야에서 클로저(closure)로 알려져 있습니다. 


모든 자바스크립트 함수는 클로저입니다.

때때로 함수 호출 간에도 값을 유지할 수 있는 함수를 작성하고 싶을 때가 있습니다. 

이를 위해 지역 변수를 사용할 수는 없는데, 호출 객체가 함수 호출 간에 유지되지 않기 때문입니다. 

클로저를 이용하면 영속적이고 비공개적인 변수를 만들어낼 수 있다. 


다음 예제를 살펴봅니다.

javascript
getUniqueID = (function(){
    var id = 0;
    return function(){ 
        return id++; 
    }
})();
 
console.log("id: " + getUniqueID());
console.log("id: " + getUniqueID());
console.log("id: " + getUniqueID());

// 위 코드를 실행한 결과는 다음과 같다.
id: 0
id: 1
id: 2


또 다른 흥미로운 예제는 다음과 같습니다.

javascript
function makeMultiplier(x){
    return function(y){
        return x*y;
    }
}
 
var multiply10 = makeMultiplier(10);
var multiply20 = makeMultiplier(20);
 
console.log(multiply10(5));     // 50
console.log(multiply20(5));     // 100

이 예제에서는 makeMultiplier(x)라는 함수를 정의했는데, 이 함수는 x라는 인자를 하나 받아 새로운 함수를 반환합니다. 

이 함수에서 반환한 함수는 y라는 인자를 받아 x*y 를 반환하고 있습니다. 

본질적으로 makeMultiplier는 함수 팩터리입니다. 

즉, 특정 값을 그것의 인자와 곱할 수 있는 함수를 만들어냅니다. 


위 예제에서는 함수 팩터리를 이용해 두 개의 새로운 함수를 만들어냈습니다. 

하나는 인자에 10을 곱하는 함수(변수 multiply10 에 makeMultiplier(10) 로 인한 반환된 함수)이고, 다른 하나는 20을 곱하는 함수(변수 multiply20 에 makeMultiplier(20) 로 인해 반환된 합수)입니다. 

multiply10과 multiply20은 모두 클로저라고 할 수 있습니다. 

두 함수는 동일한 함수 본문 정의를 공유하지만 유효범위 체인은 다르다는 것입니다. 

multiply10의 유효범위 체인에서는 x가 10이고 multiply20의 경우에는 x가 20인 것입니다.




모듈(module)과 네임스페이스(namespace)

자바스크립트에서 전역 변수를 정의할 때마다 해당 변수가 다른 코드에 의해 덮어쓰일 수 있다는 위험을 항상 가지고 있습니다. 

따라서 모듈을 작성하고 싶다면 전역 변수를 정의하는 일을 피해야 하고 여러분의 작성하는 모듈에 대한 메서드와 프로퍼티를 모두 해당 모듈에 특화해서 만든 네임스페이스 안에 정의해야 할 필요성을 지닙니다. 


자바스크립트에는 네임스페이스에 대해 특별히 지원하는 바가 없지만 자바스크립트 객체를 그러한 목적으로 사용할 수 있습니다. 

먼저 네임스페이스 객체를 정의한 다음 전역 객체가 아닌 해당 네임스페이스 객체 안에 모듈 메서드를 정의하고 저장하면 됩니다.


모듈과 네임스페이스 예제는 다음과 같습니다.

javascript
// 전역 변수 정의
var itinpractice;
 
// undefined이면 그것을 객체로 만듬
if (!itinpractice) itinpractice = {}
 
// 네임스페이스 생성: itinpractice.Module1
itinpractice.Module1 = {}
 
// 네임스페이스를 메서드로 채움
itinpractice.Module1.method1 = function(){
    console.log("This is itinpractice.Module1.method1");
}
itinpractice.Module1.method2 = function(){
    console.log("This is itinpractice.Module1.method2");
}
 
// 메서드 호출
itinpractice.Module1.method1(); // This is itinpractice.Module1.method1
itinpractice.Module1.method2(); // This is itinpractice.Module1.method2





Jaehee's WebClub



[출처] 코딩누리