본문 바로가기
Script/JavaScript

[JavaScript] Chapter 19. 클로저

by song.ift 2022. 12. 17.

클로저 (Closure)

  • 함수와 함수가 선언된 어휘적 환경(렉시컬 환경)의 조합 => 내부함수가 외부함수 변수에 접근 가능

 

렉시컬 스코프 이해

function init() {
    // name은 init에 의해 생성된 지역 변수
    var name = "Mozilla";
    
    // displayName()은 내부 함수이며, 클로저
    function displayName() {
        // 부모 함수에서 선언된 변수를 사용
        alert(name);
    }
    
    displayName();
}
init();

 

은닉화

  • Information Hiding
  • 외부로부터 데이터를 감추는 것
  • private로 감춤
  • 내부적으로 사용해야하는 경우 감춤
// ex) IIFE (즉시 실행 함수)
(function() {
    var str = "테스트";
})();
console.log(str);  // 외부에서 접근 불가능 = 은닉화


// 은닉화 전
function printFunc(name) {
    this._browser = name;
}

printFunc.prototype.print = function() {
    console.log(this._browser);
}

var browser1 = new printFunc("크롬");
browser1.print();
browser1._browser = "모질라";  // 쉽게 접근 가능
browser1.print();


// 은닉화 후
function printFunc(name) {
    var _browser = name;   // 외부 접근 불가능한 private 하게 사용
    
    function print() {
        console.log(_browser);
    }
    return print;
}

var browser1 = new printFunc("크롬");
browser1();

   

클로저의 올바른 정의

함수와 함수가 선언된 어휘적(lexical) 환경의 조합

  • 클로저는 함수를 지칭하고 또 그 함수가 선언된 환경과의 관계라는 개념이 합쳐진것이다.

클로저의 핵심은 스코프를 이용해서, 변수의 접근 범위를 닫는(폐쇄)것에 있다.

  • 외부함수 스코프에서 내부함수 스코프로 접근 불가능하다.
  • 내부함수에서는 외부함수 스코프에서 선언된 변수에 접근 기능하다.
  • 따라서 내부 함수는 외부함수에 선언된 변수에 접근 가능하다.

함수가 호출되는 환경과 별개로, 기존에 선언되어 있던 환경(어휘적 환경)을 기준으로 변수를 조회한다.

  • 외부함수의 실행이 종료된 후에도, 클로저 함수는 외부함수의 스코프, 즉, 함수가 선언된 어휘적 환경에 접근할 수 있습니다.
  • 외부 함수 스코프가 내부함수에 의해 언제든지 참조될 수 있다.
  • 따라서 클로저를 남발할 경우 퍼포먼스 저하가 발생할 수도 있습니다.

상위 스코프의 식별자를 포함하여 쓰여있는 내부 함수 코드 자체를 어휘적 환경(lexical environment)라고 부를 수 있다.

 

 클로저의 혼란스런 정의

클로저는 내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 가리킨다?

  • 내부함수(innerFn)가 상위 스코프(예:outerFn)의 식별자를 참조하고 있고, 그 내부함수(innerFn)를 그 상위 스코프(예:outerFn) 바깥에서 사용했을 때, 그 상위 스코프(예:outerFn)의 식별자를 수정할 수 없는 형태이다.
  • 즉, 함수가 정의되었던 위치에서 상위 스코프(예:outerFn)식별자를 기억하고 있는 형태이다.
  • 그래서 중첩함수 뿐만 아니라 내부함수가 외부 스코프에 접근할 수 있는 형태의 클로저도 존재한다.
  • 아래와 같은 클로저의 두가지 예시가 있기 때문이다.
 
// 클로저를 만드는 형태 1. - 중첩함수
function outerFn() {
  let x = 10;

  return function innerFn(y) { // innerFn 함수는 클로저다.
    return x = x + y;
  }
}

let a = outerFn(); // 외부함수 호출은 한번만. 이제 a 변수는 innerFn 함수를 참조한다.
a(5); // 15;
a(5); // 20;
a(5); // 25;
// 클로저를 만드는 형태 2. - 전역에 선언한 변수를 박스 안에서 함수로 정의하고 전역에서 호출
let globalFunc;

{
  let x = 10;
  globalFunc = function(y) { // globalFunc 함수는 클로저다.
    return x = x + y;
  }
}

globalFunc(5); // 15;
globalFunc(5); // 20;
globalFunc(5); // 25;

 

클로저는 함수를 리턴하는 함수이다?

  • 함수를 리턴하는 함수가 아니라 내부함수(innerFn)가 상위 스코프(예:outerFn)의 식별자를 참조하고 있고 그 상위 스코프(예:outerFn) 바깥에서 사용했을 때 그 상위 스코프(예:outerFn)의 식별자를 수정할 수 없는 형태이다.

클로저는 외부함수와 내부함수에 의해서 스코프가 분리된다?

  • ‘외부함수와 내부함수에 의해 스코프가 분리된 클로저도 있다’가 맞다.
  • 박스와 내부함수에 의해 스코프가 분리된 클로저도 있다.

댓글