클로

클로저 에서 JavaScript

1개의 연습 문제

클로저 소개

_클로저_는 JavaScript의 프로그래밍 패턴으로, 바깥쪽 렉시컬 스코프의 변수를 중첩된 코드 블록 안에서 사용할 수 있게 해줘요. JavaScript는 클로저를 자연스럽게 지원하기 때문에, 클로저가 무엇인지도 모른 채 사용하는 경우가 많아요.

// Top-level declarations are global-scope
const dozen = 12;

// Functions create a new function-scope and block-scope.
// Referencing the outer variable here is a closure.
function nDozen(n) {
  return dozen * n;
}

_클로저_라는 이름은 역사적으로 λ-계산법에서 유래했으며, 함수의 열린 변수 바인딩과 닫힌 변수 바인딩을 가리키기 위해 scheme에 의해 널리 퍼졌어요 (출처).

JavaScript에서 클로저를 사용하는 이유

  1. 데이터 프라이버시 / 데이터 캡슐화

    • 다른 언어와 달리, 2020년에는 private 변수를 지정할 방법이 없었어요. 그래서 클로저를 사용하면 private 변수를 효과적으로 흉내 낼 수 있었어요 (private 변수 표기법을 도입하자는 제안이 있었는데, 이 글을 읽을 때쯤이면 표준이 되었을 수도 있어요).
    // Consider this function:
    
    const saveNumber = (number) => {
      // The saved value is unaccessible by the outer lexical scope.
      const value = number;
    
      // We can provide access to the primitive value with a function, but the original will never change
      return () => value;
    };
    
    // Attempting to set the variable outside of its lexical scope results in an error
    value = 42;
    
  2. 부분 적용

    • 함수는 함수를 반환할 수 있는데, 반환된 함수가 자신을 만든 함수의 인자를 사용하는 것이 바로 클로저로 부분 적용을 하는 예예요. 이를 두고 함수를 _커링_한다고 부르기도 해요.
    // The arguments are applied one-by-one, using closures to arrive at the final result
    function partialBuildUri(scheme) {
      return function (domain) {
        return function (path) {
          return `${scheme}://${domain}/${path}`;
        };
      };
    }
    
    // A function could apply them all at once.
    function buildUri(scheme, domain, path) {
      return partialBuildUri(scheme)(domain)(path);
    }
    
    // Or apply a few to create a flexible system of functions for reuse.
    function buildHttpsExercismUri(path) {
      return partialBuildUri('https')('exercism.org');
    }
    

변수의 렉시컬 스코프

모든 프로그래밍 언어에서 클로저는 변수 스코프와 밀접하게 관련되어 있어요. JavaScript에는 변수에 대한 렉시컬 스코프가 세 가지 있어요:

  1. 전역 스코프: 함수와 블록 바깥에서 선언된 변수예요.
  2. 함수 스코프: var 키워드로 함수 안에서 선언된 변수예요.
  3. 블록 스코프: let과 const 키워드로 블록({...}) 안에서 선언된 변수예요.

실제로 클로저는 함수(또는 블록)가 바깥 스코프의 변수를 사용할 때 자주 생겨요. JavaScript에서는 이것이 자연스럽게 지원되기 때문에, 클로저를 특별히 의식하지 않고 사용하는 경우가 많아요.

함수 스코프

var 키워드는 함수 스코프 변수를 정의해요. 즉, var 키워드로 정의된 변수는 그것이 정의된 함수 안 어디에서나, 그리고 중첩된 모든 렉시컬 스코프(함수든 블록이든)에서 사용할 수 있어요.

{
  var five = 5;
}

// This will succeed because 'five' exists in the same scope
var tenTimes = five * 10;

블록 스코프

let과 const 키워드는 블록 스코프 변수를 정의해요. let은 변경 가능한 변수를 정의하는데, 값이 여러 번 설정될 수 있고 정의만 하고 초기화하지 않을 수도 있어요. const는 정의할 때 반드시 초기화해야 하는 변수를 정의하며, 그 값은 이후에 바뀔 수 없어요. const 변수에 원시값을 할당하면 그 값을 변경할 수 없어요. const 변수에 _배열_이나 객체 같은 참조값을 할당하면 참조는 바뀔 수 없지만 배열의 내용은 변경할 수 있어요.

다음 블록을 살펴봐요:

// 'two' is defined at the top-level, therefore available to the global-scope
const two = 2;

{
  // This succeeds, as 'two' is available from the outer lexical scope
  const oneLess = two - 1;
}

// This throws an error because oneLess is not defined outside of the previous inner lexical scope
console.log(oneLess);

중괄호({...})를 사용해 블록을 마음대로 만들 수 있지만, 중괄호는 주로 if 문, 함수, _클래스_를 감쌀 때 볼 수 있어요.

const PI = 3.14;

// This function uses a closure for the value of PI (π)
function areaOfCircle(radius) {
  return PI * radius * radius;
}
GitHub에서 편집 링크가 새 창이나 탭에서 열려요

클로저 배우기