Posts JavaScript - 자바스크립트 클로저(Closure)와 즉시 실행 함수(IIFE)
Post
Cancel

JavaScript - 자바스크립트 클로저(Closure)와 즉시 실행 함수(IIFE)


JavaScript - 자바스크립트의 클로저(Closure)란?


🚀 무엇을 클로저(Closure)라 부르는 것인가?

(읽기에 앞서, 실행 컨텍스트나 스코프에 관한 내용을 아래 포스팅을 통해 보고 와 주시면 더욱 좋습니다.)

자바스크립트의 실행 컨텍스트, 스코프 : https://chanhuiseok.github.io/posts/js-4/

아래와 같은 코드가 있습니다.

1
2
3
4
5
6
7
8
9
10
function outerFunc(){
    var x = 1;
    return function() {
        console.log(x);
    }
}
var inner = outerFunc();
// outerFunc의 실행 컨텍스트는 종료 //

inner(); // 1

여기서 outerFunc의 내부 함수에 주목할 필요가 있습니다.

outerFunc 함수는 변수 inner에 대입되고, 이후에는 종료되었으니 실행 컨텍스트가 없어질 것입니다. 그러나 outerFunc가 반환한 내부 함수에서는 x를 여전히 참조할 수 있고, 따라서 1이 출력될 수 있었습니다.

어떻게 가능했을까요? 이것은 실행 컨텍스트, 스코프 등으로 미리 추측하실 수 있습니다.

이제 위 코드의 실행 컨텍스트가 어떻게 되는지 분석해 보겠습니다.


  • 전역 실행 컨텍스트의 변수 객체 안에는 outerFunc, inner, [[scope]] 프로퍼티가 있음.
    • [[scope]]는 전역 객체를 가리키고 있는 상태

  • outerFunc 실행 컨텍스트의 변수 객체 안에는 변수 x (x : 1), 익명 함수, [[scope]] 프로퍼티가 있음
    • outerFunc 실행 컨텍스트 변수 객체의 [[scope]] 프로퍼티는 outerFunc 변수 객체 -> 전역 객체 를 가리키고 있는 상태

  • inner() 실행 시, inner 실행 컨텍스트가 생성되고 그 변수 객체의 [[scope]] 프로퍼티는 자신 외부 함수의 [[scope]] 프로퍼티를 그대로 가지며, 거기에 더해 inner 변수 객체 를 추가로 가진다.
    • 즉, inner 실행 컨텍스트 변수 객체의 [[scope]] 프로퍼티는 inner 변수 객체 -> outerFunc 변수 객체 -> 전역 객체 로 가리키고 있는 상태이다.
    • 따라서, 가장 먼저 inner 변수 객체에 변수 x가 있나 보고, 없으니 스코프 체인에 의해 다음 스코프인 outerFunc 변수 객체를 확인한다. 거기에는 변수 x (x:1) 이 있었으므로, 그 값을 참조하게 된다.

즉, 이미 생명주기가 끝난 외부 함수의 변수를 참조하는 내부 함수클로저라고 부릅니다. 그리고 그 외부 함수에 있던 변수를 자유 변수라고 부릅니다.

위의 코드에서는 변수 x가 자유 변수 이고, outerFunc() 내부에 리턴문에 작성된 익명 함수가 클로저 가 되는 것입니다.

이처럼 클로저 외부의 함수 실행 컨텍스트가 사라져도, 클로저는 여전히 그 외부 함수의 변수 객체를 스코프 체인으로 가지고 있었기 때문에, 외부 변수를 참조할 수 있던 것입니다.

클로저(Closure) : 함수가 자유 변수에 대해 닫혀 있다는 의미로 이러한 이름이 붙었습니다.


클로저를 사용할 경우 위와 같이, 접근하려는 변수가 대부분 외부 함수에 있는 자유 변수 이므로 스코프 체인에서 첫 번째 객체(즉 자기 자신)가 아닌 그 이후의 객체에 존재하게 됩니다.

이는 스코프 체인의 뒤쪽 객체에 자주 접근하는 것이므로 성능을 저하시킬 수 있는 요인이 되기도 합니다. 그래서 클로저를 무분별하게 사용하는 것이 아니라 적재적소에 알맞게 잘 사용하는 것이 중요합니다.


🚀 클로저의 활용

  • 함수의 캡슐화

클로저를 통해 함수의 캡슐화, 그 중에서도 은닉성을 구현할 수 있습니다.

함수 안에서 사용될 변수 등을 전역으로 선언해 놓으면, 그 변수들은 그대로 노출이 되어 어디서든 쉽게 임의로 그 값을 바꿀 수 있을 것입니다. 또는 이와 같은 이름의 변수를 중복 선언해서 충돌이 발생할 수도 있습니다. 그래서 라이브러리 같은 것을 만들 때에는 이러한 혼잡성을 없애기 위해서, 클로저나 즉시 실행 함수 패턴 등을 적극 활용하게 됩니다.

함수 내부에서만 사용하고 싶은 private 변수를 정의하고 싶을 때 클로저를 활용하면 됩니다.

1
2
3
4
5
6
7
8
function outerFunc(age){
	var _age = age;
	return function(){
		console.log(_age);
	}
}
var innerFunc = outerFunc(20);
innerFunc(); // 20

outerFunc 내부에 클로저를 구현한 모습입니다. 여기서 자유 변수는 _age 입니다. 즉, _age 변수는 private 변수처럼 사용할 수 있게 된 것입니다. (_가 포함된 변수는 private 변수에 주로 붙입니다)

외부에서는 _age에 대해 직접 접근할 방법이 없습니다. 오직 클로저에서만 _age를 사용할 수 있습니다. 이렇게 은닉화를 클로저를 통해 쉽게 구현할 수 있게 되었습니다.


  • 특정 상태를 기억하고, 그 상태를 최신으로 유지하는 경우

(이 내용은 해당 출처를 참고하였습니다 : https://poiemaweb.com/js-closure)

클로저에서는 자유 변수 값을 자유롭게 사용할 수 있고, 변경도 가능합니다. 즉, 외부 함수의 실행 컨텍스트는 없어졌지만 클로저는 그 자유 변수 값을 자유롭게 쓸 수 있다는 의미입니다. 따라서

1
2
3
4
5
6
7
8
9
10
var toggle = (function () {
      var isShow = false;

      //  클로저를 반환
      return function () {
        box.style.display = isShow ? 'block' : 'none';
        //  상태 변경
        isShow = !isShow;
     };
})();

위의 toggle 함수를 특정한 DOM 요소에 이벤트로 할당해 줄 것입니다. 이벤트 내용은, 버튼을 클릭할 때마다 요소가 보이거나, 보이지 않거나 하는 것입니다.

여기서 isShow 변수는 자유 변수이고, 밑에서 클로저를 작성해 반환하고 있습니다.

  • 처음 버튼을 누를 때는 isShow 값이 false이므로 박스가 사라집니다. 그 다음, 클로저 안에서 isShow 자유 변수의 값을 true로 바꿉니다.

  • 클로저 외부의 익명 함수는 실행 컨텍스트가 사라졌더라도, 클로저의 스코프 체인에서 이미 해당 변수 객체를 가지고 있으므로 여전히 isShow를 자유롭게 사용 가능합니다. 따라서, 값의 변경도 가능합니다.

  • 다시 버튼을 누르면, isShow 값이 true이므로 박스가 보여집니다. 그 다음, 클로저 안에서 isShow 자유 변수의 값을 false로 바꿉니다.

이처럼 isShow 변수가 변경되어도 최신의 값으로 유지할 때 클로저가 유용하게 활용될 수 있습니다. 만약 isShow 변수를 전역 변수로 선언했다면, 어디에서나 이 변수에 접근이 가능해집니다. 따라서 코드에서 실수로 isShow 변수를 원치 않는 곳에서 바꿀 경우 코드가 의도대로 동작하지 않을 수 있습니다.


🥝 즉시 실행 함수(IIFE, Immediately-invoked function expression, Immediate functitons)

참고로 위 코드의 toggle 함수에 대입된 익명 함수는 즉시 실행 함수 패턴으로 작성된 것입니다. 함수 바깥에 () 로 감싸고, 그 옆에 () 안에는 즉시 실행함수에 넘길 인자값을 작성하는 것입니다. 여기서는 별도의 인자는 넘기지 않았습니다.

즉시 실행 함수함수가 정의되면서 동시에 바로 실행합니다. 이후 이 함수는 다시 호출할 수 없습니다. 함수만의 유효 범위를 가지게 되는 효과를 볼 수 있으므로, 라이브러리들이 최초 한 번의 실행만 필요로 하는 초기화 코드 등에서, 변수 중복 문제나 임의 접근 문제 등을 피하기 위해 많이 사용됩니다.

1
2
3
4
5
6
(function (name){
    var x = 'hui';
    console.log('Hello ' + name + x)
})('chan'); // 'Hello chanhui'

console.log(x); // x is not defined

즉시 실행 함수 부분은 정의한 뒤 바로 실행되어 Hello chanhui를 출력하고, 그 외부에서 변수 x에 접근한다면 오류가 발생합니다.


본 포스팅은 인사이드 자바스크립트(송형주, 고현준 저)의 내용을 참고하여 작성하였습니다.

쿠키와 세션의 차이는 무엇일까?

React, vuejs, angular git repository 비교해보기