인사이드 자바스크립트를 공부하며 정리하는 포스팅입니다.



클로저


이미 생명 주기가 끝난 외부 함수의 변수를 참조하는 함수를 클로저라고 합니다.

클로저로 참조되는 외부 변수를 자유 변수(Free variable)라고 합니다. closure라는 이름은 함수가 자유 변수에 대해 닫혀 있다는 의미입니다.


//closure 예제
function outerFunc(){
    var x = 1;

    return function(){
        x = x * 2;
        console.log(x);
    };
};

var inner = outerFunc();
// outerFunc 실행 컨텍스트 종료

inner();


위와 같은 코드가 클로저를 구현하는 전형적인 패턴입니다. 외부 함수가 호출되고 이 외부 함수에서 반환된 함수가 클로저입니다.


클로저에서 실행 컨텍스트가 종료된 외부 함수의 변수를 접근할 수 있는 것은,

스코프 체인에서 참조하고 있는 것이 외부 함수의 변수 객체이기 때문입니다. outerFunc의 실행 컨텍스트는 사라젔지만 변수 객체는 여전히 남아있고, inner의 스코프체인으로 참조되고 있습니다.



클로저의 활용


클로저는 스코프체인에서 뒤쪽에 있는 객체에 자주 접근하므로, 성능 저하의 원인이 되기도 합니다. 그러나, 그렇다고 클로저를 쓰지 않는 것은 자바스크립트의 강력한 기능 중 하나를 무시하는 것이므로 잘 사용하는 것이 중요합니다.


1) 특정 함수에 사용자가 정의한 객체의 메서드 연결하기


정해진 형식의 함수를 콜백해주는 라이브러리가 있을 경우, 그 형식과는 다른 형식의 사용자 정의 함수를 호출할 때 유용하게 사용할 수 있습니다.


var Person = function(name){
    this.name = name;
    this.func = null;
};

Person.prototype.introduce = function(){
    this.func ? this.func(this.name): null;
};

Person.prototype.setIntroduce =function(func){
    this.func = func;
}

Person이라는 객체는 introduce라는 함수가 구현되어 있습니다.

introduce는 사용자가 등록한 함수를 호출해줍니다.


var userFunc = function(name){ console.log(name); }; var song = new Person("Song"); song.setIntroduce(userFunc); song.introduce(); //"Song"

사용자가 userFunc라는 함수를 구현하고, Person객체를 만들어서 등록하였습니다.

introduce()를 호출하면 "Song"이 출력됩니다.

간단한 콜백함수의 예입니다.


그런데 사용자가 등록할 수 있는 콜백함수는 name인자 하나를 받도록 형식이 정해저 있습니다.

만약 age를 인자로 추가로 받도록 하려면 어떻게해야 할까요?


클로저를 활용해서 할 수 있습니다.


var createIntroduce = function(obj, methodName, age){ return (function(name){ return obj[methodName](name, age); }); } var newObj = function(obj, age){ obj.setIntroduce(createIntroduce(this, "introduce", age)); return obj; } newObj.prototype.introduce = function(name, age){ console.log(name + ", " + age); }; var song2 = new newObj(song, 29); song2.introduce(); // "Song, 29" 출력


newObj는 Person 객체를 좀 더 자유롭게 사용하기위해 정의한 함수입니다.

위와 같이 클로저의 특성을 이용해서 age도 인자로 받을 수 있도록 할 수 있습니다.


2) 함수의 캡슐화


다음과 같이 전역 변수에 접근을 하는 함수 예제가 있습니다.

이러한 방식의 단점은 전역 변수가 외부에 노출되어 있어서, 다른 함수에서 이 값을 변경할 수도 있고 실수로 같은 이름의 변수를 만들어서 버그가 생길 수도있습니다.

var year = 2018; var month = 5; var day = 12; var sayDate = function(){ console.log(year + "년 " + month + "월 " + day + "일"); }; sayDate();


클로저를 활용해서 전역변수를 추가적인 스코프에 넣고 사용하면 이런 문제를 해결할 수 있습니다. 

sayDate에 익명의 함수를 즉시 실행시켜 반환되는 함수를 할당합니다. 

반환되는 이 함수는 클로저가 되고, 이 클로저는 자유 변수 year, month, day를 참조할 수 있다.


var sayDate = function(){
    var year = 2018;
    var month = 5;
    var day = 12;
    return (function(year, month, day){
        console.log(year + "년 " + month + "월 " + day + "일");
    });
}();

sayDate(); // "2018년 5월 12일"


클로저의 활용시 주의사항


클로저는 강력한 기능이지만 주의해서 사용해야 합니다. 



1) 클로저의 프로퍼티 값이 쓰기 가능하므로 그 값이 여러 번 호출로 항상 변할 수 있다.


2) 하나의 클로저가 여러 함수의 스코프 체인에 들어가 있는 경우도 있다.


3) 루프 안에서 클로저를 활용할 때는 주의하자.