development

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

big-blog 2020. 2. 22. 11:43
반응형

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


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


변수 범위

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

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폐쇄 내에 있습니다.


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을 호출하면 덧셈을 수행 할 값을 찾을 위치를 알 수 있습니다.


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


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

예를 들어, 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가 호출 될 때 내부 함수가 있어야 하기 때문에 문제 가됩니다! 다시 말해, 랩퍼 함수 보다 오래 지속 되고 필요할 때 거기에 있도록 변수의 클로저에서 변수 가 필요합니다. 따라서 내부 함수는 이러한 변수 스냅 샷 을 만들어서 닫히고 나중에 사용할 수 있도록 안전한 곳에 보관해야합니다. 호출 스택 외부에 있습니다.

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


클로저는 다른 함수의 상태를 참조 할 수있는 함수입니다. 예를 들어, 파이썬에서는 "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를 저장합니다. 그런 다음 (+ 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가 끝나면 이런 의미에서 x는 일시적으로 로컬 값으로 가려져 있습니다.

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

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

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

(define x 3)

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

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

x를 3으로 정의하고 더하기 x를 매개 변수 y로 정의하고 x 값을 정의합니다. 마지막으로 우리는 x가 새로운 x에 의해 가려진 환경에서 plus-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

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


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콜백이 발생할 클로저는 여전히 해당 변수를 유지하므로 원래 매개 변수로 원래 함수를 호출 할 수 있습니다.
이것은 일종의 카레 또는 기능 장식입니다.

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


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( anonymous ) 함수를 반환 closure1하고closure2 . 보시다시피 JavaScript는 함수를 다른 엔티티 (일류 시민)처럼 취급합니다.

이름 바인딩

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

위의 예에서 :

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

익명 함수의 범위 내에서 x값에 바인딩되지 않으므로 상위 (startAt 범위 .

어휘 범위

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

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

두 가지 기술이 있습니다.

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

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

위의 예에서, JavaScript가 어휘 범위가 지정되어 있음을 알 수 있습니다. x해결되면 startAt소스 코드 (x를 찾는 익명 함수는 내부에서 정의 됨) 에 따라 상위 범위 에서 바인딩이 검색 되기 때문 입니다.startAt )를 되고 호출 스택에 기반하지 않고 함수가 호출 된 방식 (범위).

마무리 (폐쇄)

우리가 전화 할 때 우리의 예에서 startAt, 그것은 할당됩니다 (일류) 함수를 반환 closure1하고 closure2, 따라서 폐쇄가 만들어집니다, 전달 된 변수 때문에 1그리고 5내 저장됩니다 startAt반환에 첨부되어있을 것,의 범위를 ' 익명의 기능. 우리는을 통해 익명 함수를 호출 할 때 closure1closure2같은 인수 (과 3)의 값 y(즉, 그 함수의 매개 변수가로), 그러나 즉시 찾을 수 x익명 함수의 범위에 구속되지 않는 해상도가 계속 때문에, 클로저에 저장된 (어휘 적으로) 상위 함수 범위 wherex 발견은 결합 될 하나 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
}

src : 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 함수가 액세스 할 수 있습니다. 또는 함수와 함께 함수와 함께 저장된 사본이 'upvalue'.


에서 Lua.org :

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


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

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

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


클로저 다른 함수 내에 정의 된 함수가있을 때마다 내부 함수는 외부 함수에 선언 된 변수에 액세스 할 수 있습니다. 폐쇄는 예제와 함께 가장 잘 설명됩니다. 목록 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


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

        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를 해결하십시오.


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

폐쇄는 매우 쉽습니다. 다음과 같이 고려할 수 있습니다. 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 함수 + (이름 변수)-> 어휘 범위


클로저는 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는 단순함이 정말 놀랍습니다.

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


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

def outer() {
    def x = 1
    return { -> println(x)} // inner
}
def innerObj = outer()
innerObj() // prints 1

참고 URL : https://stackoverflow.com/questions/36636/what-is-a-closure



반응형