본문 바로가기
Javascript study

[JS] Closure

by 카누가 좋아요 2023. 8. 27.

📌 참고 사이트

클로저 - JavaScript | MDN (mozilla.org)

 

클로저 - JavaScript | MDN

클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.

developer.mozilla.org

 

Javascript) 클로저(Closure)란? (tistory.com)

 

Javascript) 클로저(Closure)란?

이 글은 아래 링크의 글을 공부하면서 정리한 글입니다. JavaScript 클로저(Closure) 클로저란?MDN에서는 클로저를 다음과 같이 정의하고 있다. 클로저는 독립적인 (자유) 변수를 가리키는 함수이다.

seungyooon.tistory.com

 

 

 

📋 클로저란?

함수와 함수가 선언된 어휘적 환경(렉시컬 환경)의 조합이다.

즉, 클로저함수와 그 함수가 선언된 렉시컬 스코프 간의 관계를 유지하는 것이라고 할 수 있다.

 

📍 참고하면 좋을 게시물

[JS] 실행 컨텍스트 (1) (tistory.com)

 

[JS] 실행 컨텍스트 (1)

📌 참고 사이트 [JS] Javascript의 실행 컨텍스트 (Execution Context) (tistory.com) [JS] Javascript의 실행 컨텍스트 (Execution Context) Javascript 실행컨택스트 (Execution Context) 코드가 평가되고 실행되는 환경의 추상

developingdiaryoflily.tistory.com

 

 

 

📋 클로저 예시

💻 예시 1

 

function makeFunc() {
  var name = 'Mozilla';
  function displayName() {
    alert(name);
  }
  return displayName;
}

// 1. 어떤 결과가 출력될까?
var myFunc = makeFunc();

// 2. 어떤 결과가 출력될까?
myFunc();

 

➡️ var myFunc = makeFunc();

위 코드의 경우 displayName() 함수실행되기 전에 displayName()의 외부 함수인 makeFunc()로부터 반환되어 myFunc 변수에 저장되게 된다.

(myFunc를 콘솔 창에 입력해 보면 ƒ displayName() {alert(name);} 이렇게 함수 코드 그대로가 나오는 것을 볼 수 있다.)

 

makeFunc() 실행이 끝나서 displayName 함수가 반환되고 나면 name 변수에 더 이상 접근이 불가하게 될 것이라고 생각할 수 있는데 사실은 그렇지 않다. 몇몇 언어에서는 그렇게 작동한다.

 

but JS에서는 다른 것이, 함수를 반환하면 반환된 함수가 '클로저' 라는 것을 형성하기 때문에 displayName 함수의 외부 함수인 makeFunc 함수의 환경을 참조하여 결과적으로 Mozilla가 출력되게 된다. 

 

❗ 클로저는 함수와 함수가 선언된 어휘적 환경의 조합으로, 환경클로저가 생성된 시점유효 범위 내에 있는 모든 지역 변수로 구성된다.

 

즉, 위 예시에서 myFunc은 makeFunc이 실행될 때 생성된 displayName 함수의 인스턴스(makeFunc에 의해 반환된 displayName 함수라고 생각하면 된다.)에 대한 참조이다.

displayName의 인스턴스변수 name이 있는 makeFunc 함수의 어휘적 환경에 대한 참조를 유지한다. 따라서 myFunc가 호출될 때 변수 name은 사용할 수 있는 상태가 된다.

 

 

💻 예시 2

 

function makeAdder(x) {
  var y = 1;
  return function(z) {
    y = 100;
    return x + y + z;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

// 어떤 값이 출력될까?
console.log(add5(2));
console.log(add10(2));

 

위에서 add5add10 모두 클로저이다.

 

add5 부분만을 우선 살펴보면 makeAdder(5)를 통해 x에 5가 전달되게 된다. makeAdder 함수는 function(z) { y = 100; return x + y + z;}라는 익명 함수 자체를 반환하는데, add5에 할당되는 이 반환된 익명 함수는 클로저를 형성하여 외부 함수인 makeAdder의 어휘적 환경을 참조할 수 있게 된다. 

즉, add5는 makeAdder 함수 호출 시의 환경을 기억하고 있다.

따라서 add5에서는 makeAdder 함수의 argument로 들어간 x값makeAdeer 함수 안에서 선언된 y 값을 모두 참조 가능하다.

console.log 부분에서는 add5에 인자로 2를 전달해 주어 z값이 2로 정해지게 된다. 그리고 add5 함수 내에서 y가 선언되어 있기 때문에 y 값은 1(makeAdder 환경에서 참조했던 것)에서 100으로 바뀌게 된다.

따라서 최종적으로 console.log(add5(2))에서 107 (= 5 + 100 + 2 = 107) 이 출력되게 된다.

 

console.log(add10(2))의 경우도 같은 원리로 112(= 10 + 100 + 2)가 출력되게 된다.

(makeAdder 함수에 다른 인자를 넣어주었으므로 add5와 add10은 x값에서 서로 다른 맥락적 환경을 저장한다.)

 

 

 

📋 클로저 스코프 체인

모든 클로저에는 다음과 같은 세 가지 스코프(범위)가 있다.

➡️ 지역 범위 (Local Scope, Own Scope)

➡️ 외부 함수 범위 (Outer Functions Scope)

➡️ 전역 범위 (Global Scope)

 

// 전역 범위 (global scope)
var e = 10;

function sum(a) {
  return function (b) {
    return function (c) {
      // 외부 함수 범위 (outer function scope)
      return function (d) {
        // 지역 범위 (local scope)
        return a + b + c + d + e;
      };
    };
  };
}

console.log(sum(1)(2)(3)(4));     // 20

 

위 코드에서 매개변수가 d인 가장 내부 함수 기준으로 스코프를 구분한다면 다음과 같다.

➡️ 지역 범위 : d에 할당되는 argument, return a + b + c + d + e (실행문 부분) 부분이 지역 범위에 포함된다.

➡️ 외부 함수 범위 : 가장 내부 함수 바깥에 있는 모든 함수들, 즉 sum 함수, 매개변수로 b, c를 받는 함수들이 모두 외부 함수 범위에 해당된다.

➡️ 전역 범위 : 모든 함수 바깥에 선언된 var e = 10 부분이 전역 부분이다. 

 

위에서 핵심은 내부 함수 바로 근처에 있는 외부 함수의 스코프뿐만이 아니라 클로저가 선언된 모든 외부 함수의 스코프에 내부 함수가 접근할 수 있다는 것이다.

 

 

 

📋 클로저 메모리 관리

클로저의도적으로 어떤 함수 내의 지역 변수가 계속해서 메모리를 차지하고 있기 때문에 발생하는 현상이다.

따라서 함수가 종료된 시점에도 그 함수의 지역 변수가 메모리를 계속 차지하고 있기 때문에 사용 후에는 반드시 해당 메모리 참조를 지워 메모리를 비워 주어야 한다.

'함수명 = null' 처럼 적어주면 함수 내부의 변수를 참조하고 있는 것이 없기 때문에 해당 변수는 가비지 컬렉터 대상이 되어 메모리가 회수된다. 아래 예시처럼 작성해 주면 된다.

 

// 출처: chat GPT
function createClosure() {
  var data = "Hello, world!";
  return function() {
    console.log(data);
  };
}

var closure = createClosure(); // 클로저 생성

closure(); // 클로저 사용

closure = null; // 클로저 해제

// 이제 클로저 내부에서 사용되던 data 변수는 더 이상 클로저에 의해 참조되지 않으므로 메모리 회수 가능

 

 

 

📋 클로저의 장점

➡️ 데이터 보존

클로저 함수는 외부 함수 실행이 끝난 후에도 외부 함수 내 지역변수를 사용하는 것이 가능하므로 외부 함수스코프 안에 가둔 특정 데이터를 보존하여 계속 사용할 수 있다.

 

➡️ 캡슐화

객체로 여러 개의 함수를 만들어서 return하면 그 함수들이 기억되어 계속 사용될 수 있다.

이렇게 함수를 반환하는 객체들을 만들어 그 함수 내부의 데이터에 접근을 제한할 수 있다.

 

➡️ 모듈화

클로저로 데이터와 메소드를 항상 묶어둘 수 있다.

'Javascript study' 카테고리의 다른 글

[JS] Promise  (0) 2023.08.28
[JS] IndexedDB  (2) 2023.08.27
[JS] 실행 컨텍스트 (2)  (0) 2023.08.25
[JS] 실행 컨텍스트 (1)  (0) 2023.08.25
[JS] 렉시컬 환경, this 정리  (2) 2023.08.23

댓글