0w0

클로저가 알고 싶어

동기

다음과 같은 글을 읽었다.

Javascript Closure는 Private 변수 만드는 용도인가?

사실 제목을 보고나서 Private? 응? 이건 OOP 이야기 아닌가?

클로저는 함수형 프로그래밍에서 쓰는 걸로 알고 있는데...

이렇게 생각했다.

글을 읽다보니 어렴풋이 알고 있는 클로저에 대해서 이번에 제대로 알고 싶어졌다.

글에서 이런 설명을 한다.

"많은 사람들이 Closure를 Private 변수를 만드는 용도라 한다"

그리고 이는 응용에 하나에 불과하다. (그 마저도 쓰지않는다. 이미 Class가 있으니)

또 글쓴이께서도 함수형 프로그래밍을 언급하면서 고차함수에는 클로저가 필요하다. 언급한다.

고차함수는 함수를 리턴할 수 있기 때문이다.

리턴되는 함수를 일급함수라 한다.

이 일급함수는 지연실행되어야한다.

그래서 스코프(scoop)가 변수 값을 메모리에 저장해 둬야 한다.

MDN에서도 이런 식으로 언급했던 기억이 어렴풋하게 난다.

그런데 결국 클로저가 구체적으로 뭐라 생각해야하는 것일까?

애매할 때는 그림이니 우선 그림을 검색해봤다.

(...)

전혀 모르겠다.

혹시 다른 곳에서 찾아보면 어떨까 찾아보니 다행히 있었다.

closuers

음... 뭔지는 알겠는데 그래서 어쩌라는 느낌이다.

내가 빡대가리인건가?

그래서 MDN를 글자 하나, 글자 하나 천천히 손으로 적으면서 읽어보았다.

그동안엔 대충 넘기다가 코드 활용만 읽고 넘겼기에, 가장 눈에 들어오는 문장이 첫 줄에 있었다.

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

!

클로저를 이해하기 위해서는

어휘적 환경의 조합, 유효범위를 알아야 한다.

어휘적 범위 지정?

어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다. 단어 "lexical"은 이런 사실을 나타낸다. 중첩된 함수는 외부 범위(scope)에서 선언한 변수에도 접근할 수 있다.

흠흠, 변수가 소스코드 내 어디에서 선언되었는지 고려한다.

MDN의 예시를 보면 이런 코드가 있다.

function init() {
  var name = 'Mozilla'; // name is a local variable created by init
  function displayName() {
    // displayName() is the inner function, a closure
    alert(name); // displayName() uses variable declared in the parent function
  }
  displayName();
}
init();

이 함수는 외부에서 init()에 접근할 수 있어도, name, displayName()에는 접근할 수 없다.

(그래서 private 만든다 생각할 수 있을 것 같다.)

아무튼 어휘적 범위 지정에서 범위가 중요한 단어라는 것을 알게되었다.

그래서 클로저는...?

우선 예시를 보았다,

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

var myFunc = makeFunc();
//myFunc변수에 displayName을 리턴함
//유효범위의 어휘적 환경을 유지
myFunc();
//리턴된 displayName 함수를 실행(name 변수에 접근)

흠흠, 앞의 예시랑 비슷하다.

그러나 init()과 다르게, makeFunc()를 호출하면 함수가 리턴된다.

몇몇 프로그래밍 언어에서, 함수 안의 지역 변수들은 그 함수가 처리되는 동안에만 존재한다.

makeFunc() 실행이 끝나면(displayName 함수가 리턴되고 나면) name 변수에 더 이상 접근할 수 없게 될 것으로 예상하는 것이 일반적이다.

자바스크립트는 함수를 리턴하고, 리턴하는 함수가 클로저를 형성한다.

클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다.

이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.

첫 번째 예시의 경우, myFunc은 makeFunc이 실행 될 때 생성된 displayName 함수의 인스턴스에 대한 참조다. displayName의 인스턴스는 변수 name 이 있는 어휘적 환경에 대한 참조를 유지한다. 이런 이유로 myFunc가 호출될 때 변수 name은 사용할 수 있는 상태로 남게 되고 "Mozilla" 가 alert 에 전달된다.

여기서 주시해야 하는 것은 [함수를 리턴하고 리턴하는 함수가 클로저를 형성한다] 이다.

그러니까 함수를 리턴할 때, 그 당시의 어휘적 환경 조합을 갖는다는 건데,

위의 언급한 책에서는 스냅샷을 간직한다 표현한다.

책의 예시 코드를 보자.

function makeAddfunction(amount) {
  function add(num) {
    return num + amount;
  }
  return add;
}

function makeExponetialFunction(base) {
  function raise(exponent) {
    return Math.pow(base, exponent);
  }
  return raise;
}

var addTento = makeAddfunction(10);
addTento(10); // -> 20

var raiseThreeTo = makeExponetialFunction(3);
raiseThreeTo(3); // -> 9

amount, base 변수는 더 이상 활성 스코프에 없지만,

반환된 함수를 호출하면 여전히 되살릴 수 있다.

중첩된 두 함수 add, raise가 자신의 계산 로직 뿐만 아니라,

자신을 둘러싼 모든 변수의 스냅샷을 간직하고 있기 때문이다.

// outer scope
function makeInner(param) {
  // inner scope
  return function inner(param2) {
    //function body
  };
  var additionalVars;
}

함수의 클로저는 다음 두 가지를 포함한다.

var outerVar = 'Outer'; // 전역변수

function makeInner(params) {
  var innerVar = 'Inner'; // 함수 내부의 지역 변수

  function inner() {
    // inner 함수 선언.

    // innerVar, outerVar은 inner 함수 클로저의 일부이다.

    console.log(`${outerVar}, ${innerVar}, ${params}이(가) 보여요!`);
  }

  return inner;
}

var inner = makeInner('Params'); // makeInner를 호출하면 inner 함수가 반환된다.

inner(); // inner 함수는 자신의 외부에 살아있는 평범한 함수다.

// "Outer, Inner, Params이(가) 보여요!"

책에서는 이런 식으로 설명했다.

그런데 의아하다.

책의 예시에서는 innerVar, outerVarinner 함수 클로저의 일부라 했는데,

MDN 예시에서는

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

var add5 = makeAdder(5);
var add10 = makeAdder(10);
//클로저에 x와 y의 환경이 저장됨

console.log(add5(2)); // 107 (x:5 + y:100 + z:2)
console.log(add10(2)); // 112 (x:10 + y:100 + z:2)
//함수 실행 시 클로저에 저장된 x, y값에 접근하여 값을 계산

add5와 add10은 둘 다 클로저이다.

이렇게 설명했다.

역시 내가 빡대가리라 이해가 안되는 것인지 몰라도 이해가 안된다.

다행스럽게도 책에 이런 힌트가 있었다.

클로저는 함수를 선언할 당시의 환경에 함수를 묶어둔 자료구조이다.

!

클로저는 자료구조였다.

그런데 MDN에서는 그런 말이 없었는데...

혹시 저자가 뇌피셜로 적어놓은거 아냐? 이런 생각이 들어서 찾아보았으나...

아쉽게도 명확하게 클로저는 자료구조이다. 명시하는 곳은 찾지 못했다...

(개념이나, 함수의 기능 정도로 표기가 많았다.)

클로저(closure)는 내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 가르킨다. 클로저는 자바스크립트를 이용한 고난이도의 테크닉을 구사하는데 필수적인 개념으로 활용된다. - 생활코딩

클로저(closure)는 자바스크립트에서 중요한 개념 중 하나로 자바스크립트에 관심을 가지고 있다면 한번쯤은 들어보았을 내용이다. execution context에 대한 사전 지식이 있으면 이해하기 어렵지 않은 개념이다. 클로저는 자바스크립트 고유의 개념이 아니라 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어(Functional Programming language: 얼랭(Erlnag), 스칼라(Scala), 하스켈(Haskell), 리스프(Lisp)…)에서 사용되는 중요한 특성이다. - 모던 자바스크립트 Deep Dive

클로저는 함수가 속한 렉시컬 스코프를 기억하여 함수가 렉시컬 스코프 밖에서 실행될 때에도 이 스코프에 접근할 수 있게 하는 기능을 뜻한다. - You Don't Know JS

이 글에서는 이런 정의를 내린다.

클로저 = 함수 + 함수를 둘러싼 환경(Lexcial environment)

JS 클로저 = 함수가 생성되는 시점에 생성된다. = 함수가 생성될 때 그 함수의 렉시컬 환경을 포섭하여 실행될 때 이용한다.

결과적으로 그 누구도 잘 모른다는 느낌이다.

다행히 다른 답을 준 책이 있었다.

마이클 포거스의 함수형 자바스크립트에서는 이런 표현을 한다.

클로저는 미스테리 중 하나이다. 인터네에서 자바스크립트에 관한 질문 중 약 23%가 클로저에 대한 글이다.

...중략

꽤 간단하다. 클로저는 원래 있던 곳 근처에 값을 포착하는 함수이다.

이렇게 되면 이해가 뭐가 맞는 것인지 모르겠다.

그러나 뭔가 이해가 쉬워지므로 자료구조이자, 개념이자, 기능이라 치자(...)

크게보면 개념이나 기능이나 함수나 결국 뭔가 한다는 거 아닌가?

아무튼 정리하면 이렇게 된다.

클로저는 함수형 프로그래밍에서 중요한 개념이다.

클로저를 알기 위해서는 어휘적 범위 지정/스코프을 알아야한다.

클로저는 자료구조, 개념, 기능이다.

그러나 여전히 모르겠다.

클로저를 아는 것은 아직 내게 무리인 것인가 싶다...


추가 메모

문득 가지고 있던 책 중에

더글러스 크락포드의 자바스크립트는 왜 그모양일까?가 생각났다.

여기서는 클로저를

함수는 중첩될 수 있습니다. 내부 함수 객체가 생성되면 이 객체는 자신을 생성한 외부 함수에 대한 활성 객체의 참조를 가지게 됩니다.

이렇게 함수 객체가 외부 함수에 대한 활성 객체의 참조를 가지는 방식을 클로저라 합니다.

클로저는 프로그래밍 언어의 긴 역사 속에서도 중요한 발견 중 하나입니다.

스킴 언어에서 처음 발견되었죠.

그리고 자바스크립트의 주류에까지 이르렀습니다. 클로저 덕분에 자바스크립트가 더 흥미로운 언어가 되었습니다.

클로저가 없더라면 자바스크립트는 그저 좋은 의도와 실수, 클래스 무더기로 가득 차 있었을 뿐일 것입니다.

로 적어두었으며,

Haskell 문서에서는

Closure는 컴비네이터의 반대어로, 자유변수를 이용하는 함수를 지칭, 클로저는 환경의 어떤 부분을 [닫는다]이다.

f x = (y -> x + y)

f는 클로저를 반환하며, 이는 람다 추상의 외견에 구속되어있는 변수 x가 그 정의 안에 사용되어 있기 때문이다.

재밌는 점은, x가 구속되어있는 컨텍스트는 더 이상 존재하지않으면서, 람다 추상이 x의 주변을 닫아뒀으면 존재하지 않았을 것이다.

Rust에서는

1판

흔히, 함수와 자유변수를 하나로 묶은 코드의 명확함이나 재이용에 도움이 됩니다.

자유변수는 외부의 스코프에서 와서, 함수 안에 사용될 때, 닫힌다 볼 수 있습니다.

그 덕에 이러한 정리를 클로저라 부르며, Rust는 클로저가 꽤 훌륭하고 좋게 구현되어 있습니다.

2판

환경을 캡처하는 익명함수.

Rust의 클로저는 변수에 보존하거나, 인수로 다른 함수에 전달할 수 있는 익명함수입니다.

어떤 장소에 클로저를 생성해서, 거기서 다른 문맥으로 클로저를 호출해 사용하는 것이 가능하다.

함수와 다르게, 호출된 스코프의 값을 클로저는 캡처할 수 있습니다.

여기서 클로저의 기능인 코드의 재이용이나, 동작의 커스터마이즈를 하는 방법을 생각해봅시다.

2판 한국어판

러스트의 클로저는 변수에 저장하거나 다른 함수에 인자로 넘길 수 있는 익명 함수입니다.

한 곳에서 클로저를 만들고 다른 문맥에서 그것을 평가하기 위해 호출할 수 있습니다.

함수와 다르게 클로저는 그들이 호출되는 스코프로부터 변수들을 캡처할 수 있습니다.

이 클로저 특성이 코드 재사용과 동작 사용자 정의를 어떤 식으로 허용하는지 예를 들어 보여줄 것입니다.

한국어 위키

컴퓨터 언어에서 클로저(Closure)는 일급 객체 함수(first-class functions)의 개념을 이용하여 스코프(scope)에 묶인 변수를 바인딩 하기 위한 일종의 기술이다.

기능상으로, 클로저는 함수를 저장한 레코드(record)이며, 스코프(scope)의 인수(Factor)들은 클로저가 만들어질 때 정의(define)되며, 스코프 내의 영역이 소멸(remove)되었어도 그에 대한 접근(access)은 독립된 복사본인 클로저를 통해 이루어질 수 있다.

영어 위키

프로그래밍에서 클로저(혹은 lexical closure, function closure)는 1급 함수를 갖는 언어에서 렉시컬 스코프에 의한 name binding을 구현하기 위한 수법 중 하나이다.

클로저는 함수와 환경을 담은 레코드이다.

환경은 함수의 변수(로컬에 사용하거나 포함하고 스코프에 정의한 변수)를 클로저 작성시에 name binding 된 값, 참조와 연관짓는 매핑이다.

일반 함수와 달리 클로저를 사용하면 함수가 범위 밖에서 호출되는 경우에도 함수가 해당 값, 참조의 클로저 복사본을 통해 캡처된 변수에 접근할 수 있다.

일본어 위키

함수포폐는 프로그래밍에서 함수객체의 일종

여러 언어에서 람다식과 익명함수로 이용가능한 기능/개념이다.

인수 이외의 변수를 실행시 환경이 아니라,

자기가 정의된 환경(정적스코프)에 의해서 정의되는 특징이 있다.

함수와 그것을 평가하는 환경의 쌍이 있다 말할 수도 있다.

이 개념은 적어도 1960년대 SECD 머신까지 올라가야한다.

드물지만 함수가 아니어도 환경에 묶인 자료구조를 클로저라 부르기도 한다.

클로저를 지원하는 언어에 의한 프로그래밍에서는 단순 함수 안에 함수를 정의하는 것 외에도

외부의 함수(enclosure)에 선언된 변수를 암묵적으로 내부 함수에 넣어 조작할 수 있다.

주로 이용하는 것은 전역변수의 삭제, 콜백함수의 간소화 등이 있다.

전형적인 클로저는 enclosure 내부 함수리터럴이나 네스트한 함수정의에 필요하다.

프로그래밍 언어에 따라, 그 같은 내부 함수 내의 나타나는 (자유)변수(내부 함수의 임시 인수도, 내부 함수 자신의 로컬변수도 아닌 변수)를 다루는 방법은 다르지만, 자유변수를 레시컬에(문구적으로) 참조하는 것이 클로저이다.

enclosure가 실행될 때 클로저가 형성된다.

클로저는 내부 함수 코드와 enclosure의 스코프 내의 필요한 모든 변수에 대한 참조로 이루어져있다.

클로저는 프로그램 안에 환경을 공유하기 위한 구조이다.

렉시컬 변수는 전역 네임스페이스를 점유하지 않는 점에서 전역변수와 다르다.

또 객체지향에 의한 객체의 인스턴스 변수는 객체의 인스턴스가 아니라 함수의 호출에 구속되어 진다는 점이 다르다.

클로저는 함수형 언어에서 지연평가, 캡슐화, 고계함수에 인수 등등 널리 사용되고 있다.

라이브러리 설계자는 함수(콜백함수)를 인수로 받는 함수(고계함수)를 정의해서 이용자가 자유로히 커스터마이즈할 수 있는 범용성 있는 라이브러리 함수를 제공할 수 있다.

이 때, 클로저를 고계함수의 인수로 전달해서 표기의 간략화, 고계함수의 외부 상태 참조 가능 효과가 있다.

예를 들어 컬렉션의 정렬을 행하는 함수는 비교함수를 인수로 전달함으로 이용자가 정의한 것으로 정렬할 수 있는데 클로저를 사용했다면 자유도가 더 높은 처리를 간략히 표기할 수 있다.

클로저는 지연평가(호출되기 전까지는 아무 것도 안 함)되므로 제어구조를 정의할 때도 사용할 수 있다.

예를 들어 Smalltalk 분기(if-then-else), 반복(while, for)를 포함한 모든 표준제어구조는 클로저를 인수로 갖는 메서드를 갖은 객체를 이용해서 정의되어 있다. 같은 방식으로 이용자는 자기가 만든 제어구조를 간단히 정의할 수 있다.

지연평가되는 인수처럼 그 값을 구하기 위한 것을 갖고만 있지 값 자체는 계산되지 않았다는 것을 기억하기 위해서 추가로 인수를 갖지 않는 클로저 같은 자료 구조를 사용한다. 이를 Thunk라 한다. ALGOL 60에서 변수 넘기기 구현에 의한 고안되었다.

...중략...

의미론적 차이

언어마다 스코프의 의미가 다르듯 클로저 정의도 다르다.

범용적인 정의에서 클로저가 포착한 환경이란 어떤 스코프의 모든 변수의 구속된 집합이다.

하지만 그 변수의 구속이 언어마다 다르다.

명령형언어는 변수는 값을 담기 위해 메모리 위치와 함께 구속된다

이 구속은 변화하지 않고 구속된 위치에 있는 값이 변화한다.

클로저는 구속를 포착하고 있으므로 그러한 언어에서 변수 조작은 그것이 전역변수가 이든 아니든, 같은 메모리 영역에서 실행된다. 예를 ECMAScript로 들으면

var f, g;
function foo() {
  var x = 0;
  f = function () {
    x += 1;
    return x;
  };
  g = function () {
    x -= 1;
    return x;
  };
  x = 1;
  console.log(f()); // "2"
}
foo();
console.log(g()); // "1"
console.log(f()); // "2"

함수 foo를 2개의 클로저(f,g)가 동일한 메모리에 구속 된 로컬 변수 x를 사용하고 있는 것에 주목.

한편, 많은 함수형 언어.

예를 들어 ML은 변수를 직접, 값에 구속한다.

이 때, 한 번 구속된 변수의 값을 바꾸는 방법이 없으므로 클로저 사이에 상태를 공유할 필요가 없다.

단순히 같은 값을 사용할 뿐이다.

더불어 Haskell 같이 지연평가를 사용하는 함수형 언어에는 변수가 앞으로의 계산 결과에 구속된다

foo x y = let r = x /y
          in (\z -> z + r)
f = foo 1 0
main = do putStr (show (f 123))

r은 계산 (x / y)에 구속되어 있다. 위의 예시는 0으로 나누기를 하고 있다.

하지만 클로저가 참조하고 있는 것은 그 값이 아니라 계산이므로 에러는 클로저가 실행되어 실제 그 구속를 사용할 때에 나타난다.

더 큰 차이는 정적스코프인 제어구문인데, C 스러운 언어에 있는 return, break, continue 등에서 나타난다.

ECMAScript 같은 언어에서는 이들이 클로저 마다 구속되어 구문 상 구속를 은닉한다.

즉, 클로저 안에 return은 클로저를 호출한 코드에 제어를 전달한다. 하지만 Smalltalk에서는 이런 동작은 톱레벨에서 밖에 일어나지 않으며, 클로저에 포착된다.

차이를 보면

"Smalltalk"
foo
  | xs |
  xs := #(1 2 3 4).
  xs do: [:x | ^x].
  ^0
bar
  Transcript show: (self foo) "prints 1"
// ECMAScript
function foo() {
  var xs = new Array(1, 2, 3, 4);
  xs.forEach(function (x) {
    return x;
  });
  return 0;
}
print(foo()); // prints 0

Smalltalk에 있는 ^는 ECMAScript에 return으로 보면 되는 걸 염두하고 코드를 보자.

차이는 ECMAScript에서 return은 클로저를 벗어날 수 있지만, 함수 foo는 벗어날 수 없다.

Smalltalk에서 ^는 클로저일 뿐 만 아니라, 메서드 foo도 벗어난다는 점이다.

후자의 특징이 더 높은 표현력을 갖는다.

Smalltalk의 do:는 통상 메서드라, 자연스레 제어구문이 정의된다.

ECMAScript에서 return은 의미가 다르므로 같은 목적으로 foreach라는 새로운 구문을 도입해야한다.

하지만 스코프를 벗어나 생존하는 지속에는 문제가 있다.

foo
  ^[ x: | ^x ]
bar
  | f |
  f := self foo.
  f value: 123 "error!"

위의 예에서 메서드 foo가 블록을 반환할 때, foo에서 값을 반환하려 한다. 하지만 foo의 호출은 이미 완료되었으므로 이 코드는 에러가 된다.

...중략...

구현

클로저는 전형적인 함수 코드에 포인터 함수의 작성할 때 환경 표현(예를 들면 사용가능한 변수와 그 값의 집합 등)를 포함하는 특별한 자료구조로 구현할 수 있다.

언어 처리 계통에서 실행시 메모리 모델이 모든 로컬 변수를 선형으로 스택에 확보한다면 클로저를 완벽히 구현할 수 없다.

그 이유는

  1. 클로저를 만든 함수 (enclosure)의 호출처로 복귀할 때, 클로저가 참조할 스택 상 로컬 변수(렉시컬 변수)가 해방된다. 하지만 클로저에는 렉시컬 변수가 enclosure의 종료 후에도 계속되야 할 필요가 있으므로 렉시컬 변수는 필요가 없어질 때까지 확보해야 한다.

  2. 클로저가 실행될 때에 렉시컬 변수의 스택 상 위치를 알기 어렵다. ...중략... 현대적인 Scheme 처리 언어 계통에서는 클로저에 사용될 가능성 있는 로컬 변수는 동적으로 확보하며, 그렇지 않으면 스택에 확보하는 최적화를 하는 경우가 잦다.

코딩을 지탱하는 기술

객체와 클래스 中 변수와 함수를 합쳐서 모형을 만드는 법: 방법3 클로저

클로저란?

클로저도 많은 사람이 이해하기 어려운 개념 중 하나이다. 이것은 객체적인 것을 만들기 위한 기술이다.

많은 언어에서는 상태 정보를 가지고 있는 함수를 만들 수 있다 예를 들어 호출할 때마다 숫자가 1씩 증가하는 카운터 함수를 만든다 치자

function makeCounter() {
  var count = 0;
  function push() {
    count++;
    console.log(count);
  }
  return push;
}

count = makeCounter();
count(); // 1
count(); // 2
count(); // 3

...중략...

어떻게 이런 동작이 가능한 것일까? count값을 0으로 한다 그리고 반환되는 함수 push가 대응표를 끌고 밖으로 나온다 그리고 호출할 때마다 1씩 증가한다

클로저라는 특수한 구문이 있는 것은 아니다. 함수를 함수 안에 정의하고, 내포할 수 있는 정적 스코프가 있어서 함수를 반환값으로 사용하거나 변수에 대입하며 사용한다는 개념이다. 간단한 내포 구조를 사용함으로 상태 정보를 가진 함수를 만드는 것이다.

왜 클로저라 할까?

클로저라는 명칭에서 무언가 닫는다는 느낌이 든다. 왜 클로저라 불릴까? 어떤 standard ML 학습서에는 다음과 같이 써있다.

왜 이것을 클로저라고 부를까? 그것은 자유 변수를 포함한 식을 '열린 식'이라 부르고, 그 자유 변수의 바인딩을 조합함으로 해당 식을 닫고 있기 때문이다 - Ake Wikstrom, Functional programming using standar ML, Prentic-Hall, 1987.

앞의 코드로 설명하면, 함수 push는 변수 count를 참조하지만 이것은 함수 push 안에 정의되어 있지 않다. 이것이 자유 변수

함수 push는 자유 변수를 포함하고 있기 때문에 열린 함수다. 그리고 함수 makeCounter 대응표에서는 0 값과 count가 연결되어 있다. 값에 이름을 연결하는 것을 바인딩이라 한다. 열린 함수 push가 makecounter 대응표와 세트가 됨으로, 더 이상 그 이외의 스코프에 변수 정의를 찾으러 가지 않아도 되는 완결된 상태가 된다.

이것을 닫았다라 표현한다

MDN JavaScript 재입문하기 중에는...

클로저는 JavaScript가 제공하는 가장 강력한 추상화이며, 동시에 잠재적으로 가장 혼란스러울 수 있는 개념입니다. 다음 함수는 무엇을 하는 걸까요?

클로저는 함수와 함수에 의해 생성되는 스코프 객체를 함께 지칭하는 용어입니다. 클로저는 상태를 저장할 수 있기 때문에 보통 객체를 대신하곤 합니다. 다음 글을 통해 클로저에 대한 여러 훌륭한 설명을 확인할 수 있습니다.

결론적으로 클로저란

클로저는 함수형 프로그래밍에서 중요한 개념이다.

클로저를 알기 위해서는 어휘적 범위 지정/스코프을 알아야한다.

클로저는 자료구조, 개념, 기능이다.

클로저란 호출되는 스코프로부터 변수들을 캡처할 수 있다.

클로저란 함수 객체가 외부 함수에 대한 활성 객체의 참조를 가지는 방식이다.

이렇게 정리할 수 있을 것 같다.