문제

커링(Currying)에 대해 질문했는데 클로저(closure)에 대한 언급이 있었습니다.폐쇄란 무엇입니까?카레와 어떤 관련이 있나요?

도움이 되었습니까?

해결책

가변 범위

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

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 그 이후에도 지속되도록 outer 종료되었습니다. 당시 범위에 있었던 모든 변수 inner 정의되어 지속됩니다..변수 a 폐쇄되었습니다. 폐쇄 기간 내에 있습니다.

변수는 a 전적으로 비공개입니다 fnc.이는 JavaScript와 같은 함수형 프로그래밍 언어에서 개인 변수를 생성하는 방법입니다.

짐작하시겠지만 제가 전화를 하면 fnc() 그것은의 값을 인쇄합니다 a, 이는 "1"입니다.

클로저가 없는 언어에서는 변수 a 함수가 실행될 때 가비지 수집되어 버려졌을 것입니다. outer 나갔다.fnc를 호출하면 오류가 발생했을 것입니다. a 더 이상 존재하지 않다.

JavaScript에서는 변수 a 함수가 처음 선언될 때 변수 범위가 생성되고 함수가 계속 존재하는 동안 지속되기 때문에 지속됩니다.

a 의 범위에 속한다 outer.범위 inner 범위에 대한 상위 포인터가 있습니다. outer. fnc 을 가리키는 변수이다 inner. a 한 지속된다 fnc 지속됩니다. a 폐쇄 내에 있습니다.

다른 팁

예를 들어보겠습니다(JavaScript로):

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

var x = makeCounter();

x(); returns 1

x(); returns 2

...etc...

makeCounter 함수가 하는 일은 x라고 부르는 함수를 반환하는 것입니다. 이 함수는 호출될 때마다 1씩 증가합니다.x에 매개변수를 제공하지 않기 때문에 어떻게든 개수를 기억해야 합니다.이는 어휘 범위 지정을 기반으로 어디에서 찾을 수 있는지 알고 있습니다. 값을 찾으려면 정의된 지점을 찾아야 합니다.이 "숨겨진" 값을 클로저라고 합니다.

여기 내 커링 예제가 다시 있습니다:

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

var add3 = add(3);

add3(4); returns 7

볼 수 있는 것은 매개변수 a(3)를 사용하여 add를 호출할 때 해당 값이 add3으로 정의한 반환된 함수의 클로저에 포함된다는 것입니다.이렇게 하면 add3을 호출할 때 추가를 수행할 값을 찾을 위치를 알 수 있습니다.

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

우선 여기 계신 대부분의 분들이 말씀하시는 것과는 달리, 폐쇄는 ~ 아니다 함수!그래서 뭐 ~이다 그것?
이것은 세트 함수의 "주변 컨텍스트"(해당 컨텍스트라고 함)에 정의된 기호 환경) 이를 CLOSED 표현식(즉, 모든 기호가 정의되고 값이 있으므로 평가할 수 있는 표현식)으로 만듭니다.

예를 들어 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 부르면 거기 있어야 해!즉, 클로저부터 어떻게든 변수가 필요합니다. 오래 살다 래퍼 기능을 사용하고 필요할 때 거기에 있을 수 있습니다.따라서 내부 함수는 다음을 수행해야 합니다. 스냅 사진 이를 닫고 나중에 사용할 수 있도록 안전한 곳에 저장하는 이러한 변수입니다.(호출 스택 외부 어딘가에 있습니다.)

이것이 사람들이 종종 용어를 혼동하는 이유입니다. 폐쇄 사용하는 외부 변수 또는 나중에 이러한 변수를 저장하는 데 사용되는 데이터 구조의 스냅샷을 수행할 수 있는 특별한 유형의 함수입니다.하지만 이제 그런 내용이 있다는 점을 이해하시기 바랍니다. ~ 아니다 폐쇄 그 자체는 단지 방법일 뿐입니다. 구현하다 프로그래밍 언어의 클로저 또는 함수 클로저의 변수가 필요할 때 존재할 수 있도록 하는 언어 메커니즘입니다.클로저에 대한 많은 오해가 있어 (불필요하게) 이 주제를 실제보다 훨씬 더 혼란스럽고 복잡하게 만듭니다.

클로저는 다른 함수의 상태를 참조할 수 있는 함수입니다.예를 들어 Python에서는 "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

클로저에 대한 이해를 돕기 위해 절차적 언어로 클로저를 구현하는 방법을 검토하는 것이 유용할 수 있습니다.이 설명은 Scheme의 클로저에 대한 단순한 구현을 따릅니다.

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

(define x 3)

(define y 4)

(+ x y) returns 7

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

그러나 Scheme에는 기호 값을 일시적으로 무시할 수 있는 표현식이 있습니다.예는 다음과 같습니다.

(define x 3)

(define y 4)

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

x returns 3

let 키워드가 하는 일은 x 값이 5인 새 네임스페이스를 도입하는 것입니다.여전히 y가 4이고 합계가 9로 반환되는 것을 볼 수 있습니다.또한 표현식이 끝나면 x가 다시 3으로 돌아가는 것을 볼 수 있습니다.이러한 의미에서 x는 로컬 값에 의해 일시적으로 가려졌습니다.

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

이것을 어떻게 구현할까요?간단한 방법은 연결된 목록을 사용하는 것입니다. 머리 부분에는 새 값이 포함되고 꼬리 부분에는 이전 네임스페이스가 포함됩니다.기호를 찾아야 할 때는 머리 부분에서 시작하여 꼬리 부분까지 작업합니다.

이제 일단은 일류 함수 구현으로 넘어가겠습니다.어느 정도 함수는 반환 값에서 정점에 도달하는 함수가 호출될 때 실행되는 명령 집합입니다.함수를 읽을 때 이러한 명령을 배후에 저장하고 함수가 호출될 때 실행할 수 있습니다.

(define x 3)

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

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

x를 3으로 정의하고 plus-x를 매개변수 y와 x 값으로 정의합니다.마지막으로 x가 새로운 x(값 5)로 마스크된 환경에서 plus-x를 호출합니다.함수 plus-x에 대해 연산(+ x y)만 저장하면 x가 5인 컨텍스트에 있으므로 반환된 결과는 9가 됩니다.이것이 바로 동적 범위 지정입니다.

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

(define x 3)

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

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

요약하자면, 연결 목록을 사용하여 함수 정의 시 네임스페이스의 상태를 저장할 수 있으며, 이를 통해 둘러싸는 범위에서 변수에 액세스할 수 있을 뿐만 아니라 나머지 부분에 영향을 주지 않고 변수를 로컬로 마스킹할 수 있는 기능을 제공할 수 있습니다. 프로그램.

클로저가 왜 대단한지에 대한 실제 사례는 다음과 같습니다.이것은 내 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 콜백이 발생하더라도 클로저는 해당 변수를 계속 유지하므로 원래 매개변수를 사용하여 원래 함수를 호출할 수 있습니다.
이것은 일종의 카레링 또는 기능 장식입니다.

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

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)

일류 기능

기본적으로 그 뜻은 다른 엔터티와 마찬가지로 함수를 사용할 수 있습니다..이를 수정하거나, 인수로 전달하거나, 함수에서 반환하거나, 변수에 할당할 수 있습니다.기술적으로 말하자면, 그들은 일류 시민, 따라서 이름은 다음과 같습니다.일류 기능.

위의 예에서, startAt (익명의) 기능이 할당되는 기능 closure1 그리고 closure2.보시다시피 JavaScript는 다른 엔터티(일류 시민)와 마찬가지로 함수를 처리합니다.

이름 바인딩

이름 바인딩 알아내는 것입니다 어떤 데이터가 변수인가요? (식별자) 참고자료.여기서 범위는 바인딩이 해결되는 방법을 결정하는 것이기 때문에 정말 중요합니다.

위의 예에서:

  • 내부 익명 함수의 범위에서, 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의 기본 부분입니다.

카레링

아, 그리고 당신은 또한 무엇을 배웠습니까? 카레 에 관한 것입니다:여러 매개변수가 있는 하나의 함수를 사용하는 대신 함수(클로저)를 사용하여 작업의 각 인수를 전달합니다.

자유 변수를 포함하지 않는 함수를 순수 함수라고 합니다.

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

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
}

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

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

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를 반환합니다.

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

.

폐쇄 함수가 자체 범위 변수에 액세스하고, 외부 함수 변수에 액세스하고, 전역 변수에 액세스할 수 있는 JavaScript의 기능입니다.

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

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

******************
Example of Closure
******************

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이 됩니다.

다음은 또 다른 실제 사례이며, 게임에서 널리 사용되는 스크립팅 언어인 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 함수가 이에 액세스할 수 있거나 오히려 함수와 함께 저장된 복사본에 액세스할 수 있습니다. '업밸류'.

에서 Lua.org:

함수가 다른 함수에 포함되어 작성되면 해당 함수는 포함하는 함수의 지역 변수에 대한 전체 액세스 권한을 갖습니다.이 기능을 어휘 범위 지정이라고 합니다.분명해 보일 수도 있지만 그렇지 않습니다.어휘 범위 지정과 일류 함수는 프로그래밍 언어의 강력한 개념이지만 이 개념을 지원하는 언어는 거의 없습니다.

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

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

함수 g 폐쇄입니다. g 닫는다 a 안에.그래서 g 멤버 함수와 비교할 수 있습니다. a 클래스 필드와 비교할 수 있으며 함수 f 수업으로.

클로저 다른 함수 내부에서 함수가 정의 될 때마다 내부 함수는 외부 기능에서 선언 된 변수에 액세스 할 수 있습니다.클로저는 예제를 통해 가장 잘 설명됩니다.Listing 2-18에서 내부 함수가 외부 범위에서 변수 (변수 연합)에 액세스 할 수 있음을 알 수 있습니다.외부 함수의 변수는 내부 함수에 의해 닫혔습니다(또는 바인딩되었습니다).따라서 종료라는 용어.개념 자체는 매우 간단하고 매우 직관적입니다.

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

클로저에 대해 더 자세히 이해하려면 아래 코드를 살펴보세요.

        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);          
        }

간단하게 설명하자면, 함수가 생성될 때까지 첫 번째 코드에서 for 루프를 호출할 때까지 아무 일도 일어나지 않지만 5번 호출되었지만 즉시 호출되지 않았습니다. 즉, 1초 후에 호출되면 이는 비동기식이므로 이 for 루프가 완료되기 전에 값 5를 저장합니다. var i에서 마지막으로 실행 setTimeout 기능 5번 및 인쇄 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는 값 5를 메모리에 저장했지만 해당 범위는 항상 하위 함수에 표시되므로 함수가 내부에서 실행될 때 setTimeout 다섯 번 인쇄되면 5,5,5,5,5

따라서 이 문제를 해결하려면 위에서 설명한 대로 IIFE를 사용하세요.

커링 :인수의 하위 집합만 전달하여 함수를 부분적으로 평가할 수 있습니다.이걸 고려하세요:

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

폐쇄는 매우 쉽습니다.우리는 이를 다음과 같이 고려할 수 있다:클로저 = 함수 + 어휘 환경

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

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

위의 경우 종결은 어떻게 됩니까?함수 init() 및 어휘 환경의 변수, 즉 이름.폐쇄 = 초기화() + 이름

다른 기능을 고려해보세요:

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

여기서 폐쇄는 어떻게 될까요?내부 함수는 외부 함수의 변수에 접근할 수 있습니다.displayName()은 상위 함수 init()에 선언된 변수 이름에 액세스할 수 있습니다.그러나 displayName()에 동일한 지역 변수가 있으면 사용됩니다.

마감 1: init 함수 + ( 이름 변수 + displayName() 함수) --> 어휘 범위

마감 2: displayName 함수 + ( 이름 변수 ) --> 어휘 범위

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top