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


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

당신이 호출 할 때 delay5000MS, 최초의 조각 실행 및 저장 그것의 폐쇄에 인수 전달. 그런 다음 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반환에 첨부되어있을 것,의 범위를 ' 익명의 기능. 우리는을 통해 익명 함수를 호출 할 때 closure1closure2같은 인수 (과 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.