'클로저'란 무엇입니까?


432

나는 카레에 관한 질문을하고 폐쇄가 언급되었다. 폐쇄 란 무엇입니까? 카레와는 어떤 관계가 있습니까?


22
이제 폐쇄는 정확히 무엇입니까 ??? 일부 답변에 따르면 폐쇄는 기능입니다. 어떤 사람들은 그것이 스택이라고 말합니다. 일부 답변은 "숨겨진"값이라고 말합니다. 내 이해로는 함수 + 동봉 변수입니다.
— Roland

3
클로저가 무엇인지 설명합니다 : stackoverflow.com/questions/4103750/…
— dietbuddha

또한 폐쇄 란 무엇입니까? at softwareengineering.stackexchange
— B12Toaster

클로저와 일반적인 사용 사례를 설명합니다 : trungk18.com/experience/javascript-closure
— Sasuke91

답변:


743

변수 범위

지역 변수를 선언하면 해당 변수에 범위가 있습니다. 일반적으로 지역 변수는 선언 한 블록이나 함수 내에 만 존재합니다.

function() {
  var a = 1;
  console.log(a); // works
}    
console.log(a); // fails

로컬 변수에 액세스하려고하면 대부분의 언어가 현재 범위에서 변수를 찾고 루트 범위에 도달 할 때까지 부모 범위를 통해 찾습니다.

var a = 1;
function() {
  console.log(a); // works
}    
console.log(a); // works

블록이나 함수가 완료되면 로컬 변수가 더 이상 필요하지 않으며 일반적으로 메모리가 부족합니다.

이것이 우리가 정상적으로 작동하는 방식입니다.

클로저는 영구 로컬 변수 범위입니다.

클로저는 코드 실행이 해당 블록 밖으로 이동 한 후에도 로컬 변수를 유지하는 영구 범위입니다. 클로저를 지원하는 언어 (예 : JavaScript, Swift 및 Ruby)를 사용하면 해당 변수가 선언 된 블록의 실행이 완료된 후에도 참조를 유지하는 경우에도 범위 (상위 범위 포함)에 대한 참조를 유지할 수 있습니다. 그 블록이나 기능에 어딘가에.

스코프 객체와 모든 로컬 변수는 함수에 연결되며 해당 함수가 지속되는 한 지속됩니다.

이것은 우리에게 기능 이식성을 제공합니다. 함수가 처음 정의되었을 때 범위 내에 있던 변수는 나중에 완전히 다른 컨텍스트에서 함수를 호출하더라도 나중에 함수를 호출 할 때 범위 내에있는 것으로 예상 할 수 있습니다.

예를 들어

요점을 보여주는 JavaScript의 간단한 예제는 다음과 같습니다.

outer = function() {
  var a = 1;
  var inner = function() {
    console.log(a);
  }
  return inner; // this returns a function
}

var fnc = outer(); // execute outer to get inner 
fnc();

여기에 함수 내에서 함수를 정의했습니다. 내부 함수는를 포함하여 모든 외부 함수의 로컬 변수에 액세스 할 수 a있습니다. 변수 a는 내부 함수의 범위에 있습니다.

일반적으로 함수가 종료되면 모든 로컬 변수가 사라집니다. 그러나 내부 함수를 반환하고 변수 가 종료 된 fnc후에도 지속되도록 변수에 할당하면 정의 되었을 때 범위 내에 있던 모든 변수 도 유지됩니다 . 변수 가 닫혔습니다 . 변수 가 닫힙니다.outerinnera

변수 a는 전적으로 개인용 fnc입니다. 이는 JavaScript와 같은 기능적 프로그래밍 언어로 개인 변수를 작성하는 방법입니다.

당신이 짐작할 수 있듯이, 호출 fnc()하면 a"1" 의 값을 인쇄합니다 .

클로저가없는 언어에서는 변수 a가 가비지 수집되어 함수 outer가 종료 될 때 버려졌습니다 . a더 이상 존재하지 않으므로 fnc를 호출하면 오류가 발생했습니다 .

JavaScript a에서 변수 범위는 함수가 처음 선언 될 때 작성되고 함수가 계속 존재하는 한 지속되므로 변수 가 지속됩니다.

a의 범위에 속합니다 outer. 의 범위에는의 범위에 inner대한 부모 포인터가 outer있습니다. fnc를 가리키는 변수입니다 inner. a지속되는 한 fnc지속됩니다. a폐쇄 내에 있습니다.


116
나는 이것이 꽤 좋고 이해하기 쉬운 예라고 생각했다.
— user12345613

16
멋진 설명에 감사드립니다, 나는 많은 것을 보았지만 이번에는 내가 실제로 그것을 얻었습니다.
— Dimitar Dimitrov

2
두 번째 단락에서 마지막 단락에 명시된 JQuery와 같은 라이브러리에서 이것이 어떻게 작동하는지 예를 들어 볼 수 있습니까? 나는 그것을 완전히 이해하지 못했습니다.
— DPM

6
안녕하세요 Jubbat, 예, jquery.js를 열고 첫 번째 줄을 살펴보십시오. 기능이 열린 것을 볼 수 있습니다. 이제 끝으로 건너 뛰면 window.jQuery = window. $ = jQuery가 표시됩니다. 그런 다음 기능이 닫히고 자체 실행됩니다. 이제 $ 함수에 액세스 할 수 있으며,이 함수는 클로저에 정의 된 다른 함수에 액세스 할 수 있습니다. 그 질문에 대답합니까?
— superluminary

4
웹에서 가장 좋은 설명. 생각보다 간단한 방법
— Mantis

95

JavaScript를 사용하여 예를 들어 보겠습니다.

function makeCounter () {
  var count = 0;
  return function () {
    count += 1;
    return count;
  }
}

var x = makeCounter();

x(); returns 1

x(); returns 2

...etc...

이 함수 인 makeCounter는 x를 호출 한 함수가 호출 될 때마다 하나씩 카운트되는 함수를 반환합니다. x에 매개 변수를 제공하지 않기 때문에 어떻게 든 카운트를 기억해야합니다. 어휘 범위 지정을 기반으로 찾을 위치를 알고 있습니다. 값을 찾기 위해 정의 된 지점을 찾아야합니다. 이 "숨겨진"값을 클로저라고합니다.

여기 내 커리 예가 있습니다.

function add (a) {
  return function (b) {
    return a + b;
  }
}

var add3 = add(3);

add3(4); returns 7

당신이 볼 수있는 것은 매개 변수 a (3)로 add를 호출하면 add3으로 정의 된 반환 된 함수의 클로저에 해당 값이 포함된다는 것입니다. 이런 식으로 add3을 호출하면 덧셈을 수행 할 값을 찾을 위치를 알 수 있습니다.


4
IDK, 위의 언어에서 사용한 언어 (아마 F #) 위의 예를 의사 코드로 제시 할 수 있습니까? 이것을 이해하는데 어려움을 겪고 있습니다.
— 사용자


3
@KyleCronin 감사합니다. Q : "숨겨진 값을 클로저"라고 말하는 것이 더 정확합니까? "값을 숨기는 기능이 클로저"입니까? 또는 "값을 숨기는 과정이 닫힘"입니까? 감사!

2
@RobertHume 좋은 질문입니다. 의미 상 "폐쇄"라는 용어는 다소 모호합니다. 저의 개인적 정의는 숨겨진 가치와 그에 따르는 기능의 조합이 폐쇄를 구성한다는 것입니다.
— Kyle Cronin

1
@KyleCronin 감사합니다-월요일에 중간 계획이 있습니다. :) 내 머리 속에 "클로저"컨셉을 유지하고 싶었다. OP의 질문에 대한 훌륭한 답변을 게시 해 주셔서 감사합니다!

58

카일의 대답 은 꽤 좋습니다. 유일한 추가 설명은 클로저가 기본적으로 람다 함수가 생성되는 시점에서 스택의 스냅 샷이라는 것입니다. 그런 다음 기능이 다시 실행될 때 기능을 실행하기 전에 스택이 해당 상태로 복원됩니다. 따라서 Kyle이 언급했듯이 count람다 함수가 실행될 때 숨겨진 값 ( )을 사용할 수 있습니다.


14
스택뿐만 아니라 스택 또는 힙 (또는 둘 다)에 저장되어 있는지에 관계없이 보존되는 어휘 범위입니다.
— 매트 펜윅

38

우선, 대부분의 사람들이 말한 것과는 반대로, 폐쇄는 기능 이 아닙니다 ! 그래서 입니다 그것은?
그것은 인 세트 (그 알려진 함수의 "주위 환경"의 정의 심볼 환경 그것 (즉, 각 심볼을 정의하는 값을 갖는다되는 표현식이 때문에이 평가 될 수있다) 밀폐 식 확인).

예를 들어, JavaScript 함수가있는 경우 :

function closed(x) {
  return x + 3;
}

그것은이다 폐쇄 표현 그것에서 발생하는 모든 기호는 그 안에 정의되어 있기 때문에 당신이 그것을 평가할 수 있도록, (그 의미가 명확하다). 즉, 자체 포함되어 있습니다.

그러나 다음과 같은 기능이 있다면 :

function open(x) {
  return x*y + 3;
}

그것은 인 오픈 식 그것에서 정의되지 않은 그것의 심볼들이 존재하기 때문이다. 즉 y. 이 함수를 볼 때, y그 의미와 의미를 알 수 없으며, 그 가치를 모릅니다. 따라서이 표현을 평가할 수 없습니다. 즉, y의미가 무엇인지 알 때 까지이 함수를 호출 할 수 없습니다 . 이것을 자유 변수y 라고 합니다 .

이것은 y정의를 요구하지만이 정의는 함수의 일부가 아닙니다. "주변 컨텍스트"( 환경 이라고도 함)의 다른 곳에 정의되어 있습니다 . 적어도 그것이 우리가 바라는 것입니다 : P

예를 들어, 다음과 같이 전역 적으로 정의 할 수 있습니다.

var y = 7;

function open(x) {
  return x*y + 3;
}

또는 그것을 감싸는 함수로 정의 될 수 있습니다 :

var global = 2;

function wrapper(y) {
  var w = "unused";

  return function(x) {
    return x*y + 3;
  }
}

표현에서 자유 변수에 의미를 부여하는 환경의 일부는 클로저 입니다. 이 방법 은 모든 자유 변수에 대해 누락 된 정의를 제공 하여 열린 표현식을 닫힌 표현식으로 변환 하므로 평가할 수 있습니다.

위의 예에서 내부 함수 (필요하지 않기 때문에 이름을 지정하지 않은)는 변수 가 자유 로워 개방형 표현식입니다. 정의는 함수 외부에 있습니다. . 해당 익명 함수 의 환경 은 변수 세트입니다.y

{
  global: 2,
  w: "unused",
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

이제 폐쇄 는 모든 자유 변수에 대한 정의를 제공하여 내부 기능 을 닫는 이 환경의 일부입니다 . 우리의 경우 내부 함수의 유일한 자유 변수는 이므로 해당 함수의 폐쇄는 환경의 하위 집합입니다.y

{
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

환경에 정의 된 다른 두 기호는 하지 의 부분 폐쇄 가 실행을 필요로하지 않기 때문에, 그 함수의. 그들은 그것을 닫을 필요가 없습니다 .

그 뒤에있는 이론에 대한 자세한 내용은 https://stackoverflow.com/a/36878651/434562

위의 예제에서 래퍼 함수는 내부 함수를 값으로 반환합니다. 이 함수를 호출하는 순간은 함수가 정의 된 (또는 생성 된) 순간부터 원격이 될 수 있습니다. 특히, 랩핑 기능이 더 이상 실행되지 않고 호출 스택에있는 해당 매개 변수가 더 이상 존재하지 않습니다 .P 내부 함수 y가 호출 될 때 내부 함수가 있어야 하기 때문에 문제 가됩니다! 다시 말해, 랩퍼 함수 보다 오래 지속 되고 필요할 때 거기에 있도록 변수의 클로저에서 변수 가 필요합니다. 따라서 내부 함수는 이러한 변수 의 스냅 샷 을 만들어서 닫히고 나중에 사용할 수 있도록 안전한 곳에 보관해야합니다. (콜 스택 외부의 어딘가에 있습니다.)

그렇기 때문에 사람들은 종종 폐쇄 라는 용어 를 그들이 사용하는 외부 변수의 스냅 샷을 만들 수있는 특수한 유형의 함수 또는 나중에 이러한 변수를 저장하는 데 사용되는 데이터 구조로 혼동하는 이유 입니다. 그러나 나는 당신이 그들이 이니까 이해 희망 하지 폐쇄 자체 - 그들은 단지 방법입니다 구현 필요할 때 함수의 폐쇄에서 변수가 될 수있는 프로그래밍 언어 또는 언어 메커니즘에 클로저를. 클로저에 대한 많은 오해가 있습니다.이 주제는 (불필요하게)이 주제를 실제로보다 혼란스럽고 복잡하게 만듭니다.


1
초보자에게 도움이 될 수있는 유추는 클로저가 모든 느슨한 끝을 묶는 것입니다. 이것은 사람이 클로저 를 찾을 때 수행하는 것입니다 (또는 필요한 모든 참조를 해결 하거나 ...). 글쎄, 그것은 내가 그렇게 생각하도록 도와주었습니다 : o)
— Will Crawford

나는 수년간 폐쇄에 대한 많은 정의를 읽었지만 지금까지 내가 가장 좋아하는 것으로 생각합니다. 나는 우리 모두가 이와 같은 개념을 정신적으로 매핑하는 우리 자신의 방법을 가지고 있다고 생각합니다.
— Jason S.

29

클로저는 다른 함수의 상태를 참조 할 수있는 함수입니다. 예를 들어, 파이썬에서는 "inner"클로저를 사용합니다 :

def outer (a):
    b = "variable in outer()"
    def inner (c):
        print a, b, c
    return inner

# Now the return value from outer() can be saved for later
func = outer ("test")
func (1) # prints "test variable in outer() 1

23

클로저에 대한 이해를 돕기 위해 프로 시저 언어로 어떻게 구현되는지 조사하는 것이 유용 할 수 있습니다. 이 설명은 Scheme에서 클로저를 간단하게 구현 한 후 따릅니다.

시작하려면 네임 스페이스 개념을 소개해야합니다. Scheme 인터프리터에 명령을 입력하면 표현식의 다양한 기호를 평가하고 해당 값을 가져와야합니다. 예:

(define x 3)

(define y 4)

(+ x y) returns 7

정의 표현식은 x의 자리에 값 3을, y의 자리에 값 4를 저장합니다. 그런 다음 (+ xy)를 호출하면 인터프리터는 네임 스페이스에서 값을 찾고 작업을 수행하고 7을 반환 할 수 있습니다.

그러나 구성표에는 기호 값을 임시로 재정의 할 수있는 표현식이 있습니다. 예를 들면 다음과 같습니다.

(define x 3)

(define y 4)

(let ((x 5))
   (+ x y)) returns 9

x returns 3

let 키워드의 기능은 x가 값 5 인 새로운 네임 스페이스를 도입하는 것입니다. y가 4라는 것을 여전히 볼 수 있으며, 합계는 9로 반환됩니다. 이런 의미에서 x는 일시적으로 로컬 값으로 가려져 있습니다.

절차 언어와 객체 지향 언어는 비슷한 개념을 가지고 있습니다. 전역 변수와 이름이 같은 함수에서 변수를 선언 할 때마다 동일한 효과를 얻습니다.

우리는 이것을 어떻게 구현할 것입니까? 간단한 방법은 연결된 목록을 사용하는 것입니다. 머리에는 새로운 값이 포함되고 꼬리에는 오래된 네임 스페이스가 포함됩니다. 기호를 찾아야 할 때 머리부터 시작하여 꼬리를 따라 내려갑니다.

이제는 일류 함수 구현으로 건너 뛰겠습니다. 대체로 함수는 함수가 반환 값에서 절정이라고 할 때 실행할 명령 집합입니다. 함수를 읽을 때 이러한 명령어를 씬 뒤에 저장하고 함수가 호출 될 때 실행할 수 있습니다.

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns ?

x를 3으로 정의하고 더하기 x를 매개 변수 y로 정의하고 x 값을 정의합니다. 마지막으로 우리는 x가 새로운 x에 의해 가려진 환경에서 더하기 -x를 호출합니다.이 값은 5입니다. x가 5 인 경우 반환되는 결과는 9입니다. 이것이 동적 범위 지정입니다.

그러나 Scheme, Common Lisp 및 기타 여러 언어에는 어휘 범위 지정 기능이 있습니다. 연산 저장 (+ xy) 외에도 해당 특정 지점에 네임 스페이스도 저장합니다. 이렇게하면 값을 찾을 때이 문맥에서 x가 실제로 3이라는 것을 알 수 있습니다. 이것은 폐쇄입니다.

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns 7

요약하면, 링크 된리스트를 사용하여 함수를 정의 할 때 네임 스페이스의 상태를 저장할 수 있으며, 둘러싸는 범위에서 변수에 액세스 할 수있을뿐만 아니라 나머지 부분에 영향을주지 않고 변수를 로컬로 마스킹 할 수 있습니다. 프로그램.


알겠습니다, 귀하의 답변 덕분에 마침내 폐쇄가 무엇인지 알 수 있다고 생각합니다. 그러나 한 가지 큰 의문이 있습니다. "함수 정의시 링크 된 목록을 사용하여 네임 스페이스의 상태를 저장하여 더 이상 범위에 속하지 않는 변수에 액세스 할 수 있습니다." Why do we want to access variables that are out of scope? when we say let x = 5, we want x to be 5 and not 3. What is happening?
— Lazer

@Laser : 죄송합니다. 그 문장은 의미가 없었기 때문에 업데이트했습니다. 나는 그것이 더 의미가 있기를 바랍니다. 또한 연결된 목록을 구현 세부 정보 (매우 비효율적 임)로 생각하지 말고 어떻게 수행 할 수 있는지를 개념화하는 간단한 방법으로 생각하십시오.
— Kyle Cronin

10

Closures가 엉덩이를 걷어차는 이유에 대한 실제 예는 다음과 같습니다. 이것은 Javascript 코드와 완전히 다릅니다. 설명하겠습니다.

Function.prototype.delay = function(ms /*[, arg...]*/) {
  var fn = this,
      args = Array.prototype.slice.call(arguments, 1);

  return window.setTimeout(function() {
      return fn.apply(fn, args);
  }, ms);
};

사용 방법은 다음과 같습니다.

var startPlayback = function(track) {
  Player.play(track);  
};
startPlayback(someTrack);

이제이 코드 스 니펫이 실행 된 후 5 초 후에 재생이 지연되기를 원한다고 상상해보십시오. 글쎄 그것은 쉽고 delay닫힙니다.

startPlayback.delay(5000, someTrack);
// Keep going, do other things

당신이 호출 할 때 delay와 5000MS, 최초의 조각 실행 및 저장 그것의 폐쇄에 인수 전달. 그런 다음 5 초 후에 setTimeout콜백이 발생할 때 클로저는 여전히 해당 변수를 유지하므로 원래 매개 변수를 사용하여 원래 함수를 호출 할 수 있습니다.
이것은 일종의 카레 또는 기능 장식입니다.

클로저가 없으면 함수 외부에서 변수의 상태를 어떻게 유지해야하므로 함수 외부에 코드가 논리적으로 속합니다. 클로저를 사용하면 코드의 품질과 가독성이 크게 향상 될 수 있습니다.


1
언어 나 호스트 객체를 확장하는 것은 전역 네임 스페이스의 일부이므로 일반적으로 나쁜 것으로 간주됩니다.
— Jon Cooke

9

자유 변수가없는 함수를 순수 함수라고합니다.

하나 이상의 자유 변수를 포함하는 함수를 클로저라고합니다.

var pure = function pure(x){
  return x 
  // only own environment is used
}

var foo = "bar"

var closure = function closure(){
  return foo 
  // foo is a free variable from the outer environment
}

src : https://leanpub.com/javascriptallongesix/read#leanpub-auto-if-functions-without-free-variables-are-pure-are-closures-impure


왜 이것이 마이너스입니까? 자유 변수와 바운드 변수, 순수 / 폐쇄 함수, 불완전 / 개방 함수로의 구분이 실제로는 "올바른 길에"있습니다. 닫힘).
— SasQ

나는이 없다 더 정말 아이디어. 이것이 StackOverflow가 짜증나는 이유입니다. 내 답변의 출처를 살펴보십시오. 누가 그와 논쟁 할 수 있습니까?
— soundyogi

너무 빨라서 나는 "자유 변수"라는 용어를 들어 본 적이 없다
— Kai

자유 변수를 언급하지 않고 클로저에 대해 말하기는 어렵습니다. 그냥 찾아 봐 표준 CS 용어.
— ComDubh

"하나 이상의 자유 변수를 포함하는 함수를 클로저"라고하는 것은 올바른 정의가 아닙니다. 클로저는 항상 일류 개체입니다.
— ComDubh

7

tl; dr

클로저는 함수와 그 범위가 변수에 할당되거나 변수로 사용됩니다. 따라서 이름 클로저 : 범위와 함수는 다른 엔터티처럼 닫히고 사용됩니다.

심도있는 Wikipedia 스타일 설명

Wikipedia에 따르면 폐쇄 는 다음과 같습니다.

일급 함수를 사용하는 언어로 어휘 범위가 지정된 이름 바인딩을 구현하는 기술.

그게 무슨 뜻이야? 몇 가지 정의를 살펴 보겠습니다.

이 예제를 사용하여 클로저 및 기타 관련 정의를 설명합니다.

function startAt(x) {
    return function (y) {
        return x + y;
    }
}

var closure1 = startAt(1);
var closure2 = startAt(5);

console.log(closure1(3)); // 4 (x == 1, y == 3)
console.log(closure2(3)); // 8 (x == 5, y == 3)

일류 함수

기본적으로 이는 다른 엔티티와 마찬가지로 함수를 사용할 수 있음을 의미 합니다. 그것들을 수정하고, 인수로 전달하거나, 함수에서 반환하거나 변수에 할당 할 수 있습니다. 기술적으로 말하면, 그들은 일류 시민 이므로 이름 : 일류 기능.

위의 예제에서 함수가 and에 할당 된 startAt( anonymous ) 함수를 반환합니다 . 보시다시피 JavaScript는 다른 엔티티 (일류 시민)와 마찬가지로 함수를 취급합니다.closure1closure2

이름 바인딩

이름 바인딩 은 변수 (식별자)가 참조 하는 데이터 를 찾는 것 입니다. 바인딩이 해결되는 방법을 결정하는 것이기 때문에 여기서 범위는 실제로 중요합니다.

위의 예에서 :

  • 내부 익명 함수의 범위에서 y에 바인딩됩니다 3.
  • 년 startAt의 범위 x에 바인딩 1또는 5(폐쇄에 따라 다름).

익명 함수의 범위 내에서 x값에 바인딩되지 않으므로 상위 startAt범위 에서 해석해야합니다 .

어휘 범위

으로 위키 백과는 말한다 , 범위는 :

바인딩이 유효한 컴퓨터 프로그램의 영역입니다. 여기서 이름은 엔티티를 참조하는 데 사용될 수 있습니다 .

두 가지 기술이 있습니다.

  • 어휘 (정적) 범위 : 변수의 정의는 포함 블록 또는 함수를 검색 한 다음 외부 포함 블록 검색에 실패하는 경우 등으로 해결됩니다.
  • 동적 범위 지정 : 호출 기능이 검색된 다음 해당 호출 기능을 호출 한 기능 등이 호출 스택을 진행합니다.

더 설명은 이 질문을 확인 하고 위키 백과에서보세요 .

위의 예에서 JavaScript가 어휘 범위가 지정된 것을 확인할 수 있습니다. x이 문제가 해결되면 startAt소스 코드 (x를 찾는 익명 함수는에 정의 됨 startAt)를 기반으로 상위 범위 에서 바인딩이 검색되므로 호출 스택을 기반으로하지 않고 함수가 호출 된 방식 (범위).

마무리 (폐쇄)

우리가 전화 할 때 우리의 예에서 startAt, 그것은 할당됩니다 (일류) 함수를 반환 closure1하고 closure2, 따라서 폐쇄가 만들어집니다, 전달 된 변수 때문에 1그리고 5내 저장됩니다 startAt반환에 첨부되어있을 것,의 범위를 ' 익명의 기능. 우리는을 통해 익명 함수를 호출 할 때 closure1와 closure2같은 인수 (과 3)의 값 y(즉, 그 함수의 매개 변수가로), 그러나 즉시 찾을 수 x익명 함수의 범위에 구속되지 않는 해상도가 계속 때문에, 제 (어휘) 상부 함수 영역 (폐쇄에 저장 한 것) x발견은 결합 될 하나 1또는5. 이제 우리는 합계에 대한 모든 것을 알고 있으므로 결과를 반환 한 다음 인쇄 할 수 있습니다.

이제 클로저와 그 작동 방식을 이해해야합니다. 이는 JavaScript의 기본 부분입니다.

카레

아, 그리고 당신은 또한 카레 가 무엇인지 배웠습니다 . 여러 매개 변수가있는 하나의 함수를 사용하는 대신 함수 (클로저)를 사용하여 연산의 각 인수를 전달합니다.


5

클로저 는 함수가 자체 범위 변수, 외부 함수 변수 및 전역 변수에 액세스 할 수있는 JavaScript의 기능입니다.

외부 함수가 반환 된 후에도 클로저는 외부 함수 범위에 액세스 할 수 있습니다. 이는 클로저가 함수가 완료된 후에도 외부 함수의 변수와 인수를 기억하고 액세스 할 수 있음을 의미합니다.

내부 함수는 자체 범위, 외부 함수 범위 및 전역 범위에 정의 된 변수에 액세스 할 수 있습니다. 외부 함수는 자체 범위와 전역 범위에 정의 된 변수에 액세스 할 수 있습니다.

폐쇄의 예 :

var globalValue = 5;

function functOuter() {
  var outerFunctionValue = 10;

  //Inner function has access to the outer function value
  //and the global variables
  function functInner() {
    var innerFunctionValue = 5;
    alert(globalValue + outerFunctionValue + innerFunctionValue);
  }
  functInner();
}
functOuter();  

내부 함수 자체 변수, 외부 함수 변수 및 전역 변수 값의 합계 인 20이 출력됩니다.


4

정상적인 상황에서 변수는 범위 규칙에 의해 구속됩니다. 지역 변수는 정의 된 함수 내에서만 작동합니다. 폐쇄는 편의상이 규칙을 일시적으로 위반하는 방법입니다.

def n_times(a_thing)
  return lambda{|n| a_thing * n}
end

위의 코드 에서 람다 (익명 함수 작성자)가 참조 lambda(|n| a_thing * n}하기 때문에 클로저 a_thing가 있습니다.

이제 결과 익명 함수를 함수 변수에 넣습니다.

foo = n_times(4)

foo는 일반적인 범위 지정 규칙을 어 기고 내부적으로 4를 사용하기 시작합니다.

foo.call(3)

12를 반환합니다.


2

간단히 말해서 함수 포인터는 프로그램 코드베이스 (예 : 프로그램 카운터)의 위치에 대한 포인터 일뿐입니다. 반면 폐쇄 = 함수 포인터 + 스택 프레임 .

.


1

• 클로저는 서브 프로그램 및 그것이 정의 된 참조 환경입니다.

– 서브 프로그램을 프로그램의 임의의 위치에서 호출 할 수있는 경우 참조 환경이 필요합니다.

– 중첩 서브 프로그램을 허용하지 않는 정적 범위 언어에는 클로저가 필요하지 않습니다.

– 폐쇄는 서브 프로그램이 중첩 범위의 변수에 액세스 할 수 있고 어디에서나 호출 할 수있는 경우에만 필요합니다.

– 클로저를 지원하기 위해 구현은 일부 변수에 무제한 범위를 제공해야 할 수 있습니다 (하위 프로그램이 더 이상 존재하지 않는 비 로컬 변수에 액세스 할 수 있기 때문에)

예

function makeAdder(x) {
return function(y) {return x + y;}
}
var add10 = makeAdder(10);
var add5 = makeAdder(5);
document.write(″add 10 to 20: ″ + add10(20) +
″<br />″);
document.write(″add 5 to 20: ″ + add5(20) +
″<br />″);

0

또 다른 실제 사례와 게임에서 인기있는 스크립팅 언어 인 Lua를 사용합니다. stdin을 사용할 수없는 문제를 피하기 위해 라이브러리 함수가 작동하는 방식을 약간 변경해야했습니다.

local old_dofile = dofile

function dofile( filename )
  if filename == nil then
    error( 'Can not use default of stdin.' )
  end

  old_dofile( filename )
end

old_dofile의 값은이 코드 블록이 범위를 끝내면 (로컬이기 때문에) 사라지지만 값이 클로저로 묶여 있으므로 새로 정의 된 dofile 함수가 액세스 할 수 있습니다. 또는 함수와 함께 함수와 함께 저장된 사본이 'upvalue'.


0

에서 Lua.org :

함수가 다른 함수로 묶여 작성된 경우, 함수를 둘러싸는 함수에서 로컬 변수에 완전히 액세스 할 수 있습니다. 이 기능을 어휘 범위 지정이라고합니다. 그것은 분명하게 들릴지 모르지만 그렇지 않습니다. 어휘 범위 및 일류 함수는 프로그래밍 언어의 강력한 개념이지만 해당 개념을 지원하는 언어는 거의 없습니다.


0

Java 세계 출신이라면 클래스의 멤버 함수와 클로저를 비교할 수 있습니다. 이 예를보십시오

var f=function(){
  var a=7;
  var g=function(){
    return a;
  }
  return g;
}

함수 g는 클로저입니다. g닫힙니다 a. 따라서 g멤버 함수 a와 비교하고 클래스 필드와 비교하거나 클래스와 함수를 비교할 수 있습니다 f.


0

클로저 다른 함수 내에 정의 된 함수가있을 때마다 내부 함수는 외부 함수에 선언 된 변수에 액세스 할 수 있습니다. 클로저는 예제와 함께 가장 잘 설명됩니다. 목록 2-18에서 내부 함수가 외부 범위에서 변수 (variableInOuterFunction)에 액세스 할 수 있음을 알 수 있습니다. 외부 함수의 변수는 내부 함수에 의해 닫혔거나 바인드되었습니다. 따라서 폐쇄라는 용어가 사용됩니다. 개념 자체는 충분히 간단하고 직관적입니다.

Listing 2-18:
    function outerFunction(arg) {
     var variableInOuterFunction = arg;

     function bar() {
             console.log(variableInOuterFunction); // Access a variable from the outer scope
     }
     // Call the local function to demonstrate that it has access to arg
     bar(); 
    }
    outerFunction('hello closure!'); // logs hello closure!

출처 : http://index-of.es/Varios/Basarat%20Ali%20Syed%20(auth.)-Beginning%20Node.js-Apress%20(2014).pdf


0

폐쇄를 더 깊이 이해하려면 아래 코드를 살펴보십시오.

        for(var i=0; i< 5; i++){            
            setTimeout(function(){
                console.log(i);
            }, 1000);                        
        }

여기에 무엇이 출력 될까요? 0,1,2,3,4그렇지 않을 것이다5,5,5,5,5폐쇄 때문이

어떻게 해결 될까요? 답변은 다음과 같습니다.

       for(var i=0; i< 5; i++){
           (function(j){     //using IIFE           
                setTimeout(function(){
                               console.log(j);
                           },1000);
            })(i);          
        }

함수가 생성 된 경우 5 번이라는 첫 번째 코드에서 for 루프를 호출 할 때까지 아무 일도 일어나지 않지만 즉시 호출되지 않았을 때 즉 1 초 후에 호출 할 때도 마찬가지입니다. var i에서 마지막으로 실행 setTimeout 함수를 다섯 번 하고 인쇄하십시오.5,5,5,5,5

IIFE 즉, 즉각적인 호출 함수 표현식을 사용하여 해결하는 방법

       (function(j){  //i is passed here           
            setTimeout(function(){
                           console.log(j);
                       },1000);
        })(i);  //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4

자세한 내용은 실행 컨텍스트를 이해하여 클로저를 이해하십시오.

  • let (ES6 기능)을 사용 하여이 문제를 해결하는 또 다른 솔루션이 있지만 위의 기능은 작동합니다.

     for(let i=0; i< 5; i++){           
         setTimeout(function(){
                        console.log(i);
                    },1000);                        
     }
    
    Output: 0,1,2,3,4
    

=> 자세한 설명 :

메모리에서 for 루프 실행 그림을 다음과 같이 만드십시오.

루프 1)

     setTimeout(function(){
                    console.log(i);
                },1000);  

루프 2)

     setTimeout(function(){
                    console.log(i);
                },1000); 

루프 3)

     setTimeout(function(){
                    console.log(i);
                },1000); 

루프 4)

     setTimeout(function(){
                    console.log(i);
                },1000); 

루프 5)

     setTimeout(function(){
                    console.log(i);
                },1000);  

여기서 나는 실행되지 않고 완전한 루프 후에 var i는 값 5를 메모리에 저장했지만 범위는 항상 자식 함수에서 볼 수 있으므로 함수가 setTimeout5 번 안에 실행될 때 인쇄됩니다.5,5,5,5,5

위의 설명에 따라이 사용 IIFE를 해결하십시오.


답변 주셔서 감사합니다. 코드를 설명과 분리하면 더 읽기 쉽습니다. (코드가 아닌 행을 들여 쓰지 마십시오)
— eMBee

0

Currying : 인수의 서브 세트 만 전달하여 함수를 부분적으로 평가할 수 있습니다. 이걸 고려하세요:

function multiply (x, y) {
  return x * y;
}

const double = multiply.bind(null, 2);

const eight = double(4);

eight == 8;

클로저 : 클로저는 함수 범위 밖에서 변수에 액세스하는 것 이상입니다. 함수 내부의 함수 또는 중첩 함수는 클로저가 아니라는 점을 기억해야합니다. 함수 범위 밖의 변수에 액세스해야 할 때는 항상 클로저가 사용됩니다.

function apple(x){
   function google(y,z) {
    console.log(x*y);
   }
   google(7,2);
}

apple(3);

// the answer here will be 21

0

폐쇄는 매우 쉽습니다. 우리는 다음과 같이 고려할 수 있습니다 : Closure = function + its lexical environment

다음 기능을 고려하십시오.

function init() {
    var name = “Mozilla”;
}

위의 경우 폐쇄는 무엇입니까? 어휘 환경의 함수 init () 및 변수, 즉 이름. 폐쇄 = init () + 이름

다른 기능을 고려하십시오.

function init() {
    var name = “Mozilla”;
    function displayName(){
        alert(name);
}
displayName();
}

여기서 폐쇄는 무엇입니까? 내부 함수는 외부 함수의 변수에 액세스 할 수 있습니다. displayName ()은 부모 함수 init ()에 선언 된 변수 이름에 액세스 할 수 있습니다. 그러나 displayName ()에서 동일한 로컬 변수가 있으면 사용됩니다.

폐쇄 1 : 초기화 함수 + (이름 변수 + displayName () 함수)-> 어휘 범위

클로저 2 : displayName 함수 + (이름 변수)-> 어휘 범위


0

클로저는 JavaScript를 상태로 제공합니다.

프로그래밍 상태는 단순히 기억하는 것을 의미합니다.

예

var a = 0;

a = a + 1; // => 1
a = a + 1; // => 2
a = a + 1; // => 3

위의 경우 상태는 변수 "a"에 저장됩니다. 우리는 1을 "a"에 여러 번 추가합니다. 우리는 그 가치를 "기억"할 수 있기 때문에 그렇게 할 수 있습니다. 상태 홀더 "a"는 해당 값을 메모리에 보유합니다.

종종 프로그래밍 언어에서는 사물을 추적하고 정보를 기억하며 나중에 액세스하려고합니다.

이것은 다른 언어 로 클래스를 사용하여 일반적으로 수행됩니다. 변수와 마찬가지로 클래스는 상태를 추적합니다. 그리고 그 클래스의 인스턴스는 차례로 그 안에 상태를 갖습니다. 상태는 단순히 나중에 저장하고 검색 할 수있는 정보를 의미합니다.

예

class Bread {
  constructor (weight) {
    this.weight = weight;
  }

  render () {
    return `My weight is ${this.weight}!`;
  }
}

"렌더링"방법에서 "무게"에 어떻게 액세스 할 수 있습니까? 주정부 덕분에 Bread 클래스의 각 인스턴스는 해당 정보를 저장할 수있는 메모리의 "상태"에서 읽음으로써 자체 가중치를 렌더링 할 수 있습니다.

이제 JavaScript는 역사적으로 클래스가없는 매우 독특한 언어 입니다 (현재는 있지만 기능과 변수 만 있습니다). 클로저는 JavaScript가 사물을 기억하고 나중에 액세스 할 수있는 방법을 제공합니다.

예

var n = 0;
var count = function () {
  n = n + 1;
  return n;
};

count(); // # 1
count(); // # 2
count(); // # 3

위의 예는 변수를 사용하여 "상태 유지"의 목표를 달성했습니다. 대단해! 그러나 이것은 변수 ( "상태"홀더)가 이제 노출된다는 단점이 있습니다. 우리는 더 잘할 수 있습니다. 클로저를 사용할 수 있습니다.

예

var countGenerator = function () {
  var n = 0;
  var count = function () {
    n = n + 1;
    return n;
  };

  return count;
};

var count = countGenerator();
count(); // # 1
count(); // # 2
count(); // # 3

이건 끝내줘.

이제 "count"기능을 셀 수 있습니다. 상태를 "유지"할 수 있기 때문에 가능합니다. 이 경우의 상태는 변수 "n"입니다. 이 변수는 이제 닫힙니다. 시간과 공간에서 휴무입니다. 시간이 지나면 복구, 변경, 가치 할당 또는 직접 상호 작용할 수 없기 때문입니다. "countGenerator"함수 내에 지리적으로 중첩되어 있기 때문에 공간에 있습니다.

왜 이것이 환상적입니까? 다른 정교하고 복잡한 도구 (예 : 클래스, 메서드, 인스턴스 등)를 사용하지 않아도 1. 멀리서 제어 할 수 있습니다.

상태 인 변수 "n"을 숨기면 프라이빗 변수가됩니다! 또한이 변수를 미리 정의 된 방식으로 제어 할 수있는 API를 만들었습니다. 특히 "count ()"와 같이 API를 호출 할 수 있으며 "거리"에서 "n"에 1을 추가합니다. 어떤 식 으로든 API를 제외하고 누구나 모양이나 형태를 "n"에 액세스 할 수 없습니다.

JavaScript는 단순함이 정말 놀랍습니다.

폐쇄는 이것이 왜 큰 부분입니다.


0

Groovy의 간단한 예는 다음과 같습니다.

def outer() {
    def x = 1
    return { -> println(x)} // inner
}
def innerObj = outer()
innerObj() // prints 1
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.