자바 스크립트 클로저와 익명 함수 시도는 다음과 같습니다. for(var

내 친구와 나는 현재 JS의 폐쇄와 그렇지 않은 것을 논의하고 있습니다. 우리는 그것을 정확하게 이해하고 싶어합니다.

이 예제를 보자. 카운트 루프가 있고 지연된 콘솔에서 카운터 변수를 인쇄하려고합니다. 따라서 카운터 변수의 값을 캡처하여 클로저 를 사용 setTimeout하여 N의 N 배를 N으로 인쇄하지 않도록합니다.

없는 잘못된 솔루션 폐쇄 또는 근처에 아무것도 폐쇄가 될 것이다 :

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

물론 i루프 후 값의 10 배 , 즉 10을 인쇄합니다.

그의 시도는 다음과 같습니다.

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

예상대로 0에서 9까지 인쇄합니다.

나는 그가 캡쳐 를 위해 클로저 를 사용하고 있지 않다고 말 i했지만 그는 자신을 주장한다. 나는 for 루프 본문을 다른 루프 안에 넣고 (익명 함수를에 전달 ) 10 번 10을 다시 인쇄 하여 클로저 를 사용하지 않는다는 것을 증명 했습니다. 나는 그의 기능을 저장하는 경우 동일하게 적용 하고 실행 한 후 내 인수가 있다는 것입니다 그래서도 10 배 (10)를 인쇄, 루프 그가 정말하지 않습니다 캡처 의 값을 자신의 버전 만들기, 하지 클로저를.setTimeoutsetTimeoutvari

나의 시도는 :

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

그래서 i( i2클로저 내에서 명명 된) 캡처 하지만 이제 다른 함수를 반환 하고 이것을 전달합니다. 필자의 경우 setTimeout에 전달 된 함수가 실제로 캡처합니다 i.

이제 누가 클로저를 사용하고 있고 누가 사용하지 않습니까?

두 솔루션 모두 콘솔에서 0에서 9까지 지연되어 원래 문제를 해결하지만이 두 솔루션 중 클로저사용 하여이를 수행하는 솔루션을 이해하고 싶습니다 .



답변

편집자 주 : JavaScript의 모든 기능은이 게시물 에서 설명한 폐쇄 입니다. 그러나 우리는 이론적 관점에서 흥미로운 이러한 기능의 하위 집합을 식별하는 데에만 관심 이 있습니다. 따라서, 단어 클로저에 대한 언급은 달리 언급되지 않는 한 이러한 기능의 서브 세트를 지칭 할 것이다.

클로저에 대한 간단한 설명 :

  1. 기능을 수행하십시오. F라고 부르겠습니다.
  2. F의 모든 변수를 나열하십시오.
  3. 변수는 두 가지 유형일 수 있습니다.
    1. 지역 변수 (바운드 변수)
    2. 비 국소 변수 (자유 변수)
  4. F에 자유 변수가 없으면 클로저가 될 수 없습니다.
  5. F에 자유 변수 ( F 상위 범위에 정의 된 )가있는 경우 :
    1. 되는 F의 부모 범위가 있어야합니다 무료 변수가 바인딩됩니다.
    2. F가 해당 상위 범위 외부에서 참조 되면 해당 자유 변수에 대한 클로저가됩니다 .
    3. 자유 변수를 클로저 F의 상위 값이라고합니다.

이제 이것을 사용하여 클로저를 사용하는 사람과 그렇지 않은 사람을 알아 봅시다 (설명을 위해 함수 이름을 지정했습니다).

사례 1 : 친구의 프로그램

for (var i = 0; i < 10; i++) {
    (function f() {
        var i2 = i;
        setTimeout(function g() {
            console.log(i2);
        }, 1000);
    })();
}

위의 프로그램에서이 두 가지 기능은 다음과 같습니다 fg. 그들이 폐쇄인지 보자.

의 경우 f:

  1. 변수를 나열하십시오.
    1. i2A는 로컬 변수.
    2. iA는 무료 변수.
    3. setTimeoutA는 무료 변수.
    4. gA는 로컬 변수.
    5. consoleA는 무료 변수.
  2. 각 자유 변수가 바인딩 된 상위 범위를 찾으십시오.
    1. i되는 바인딩 전역에.
    2. setTimeout되는 바인딩 전역에.
    3. console되는 바인딩 전역에.
  3. 어떤 범위에서 함수가 참조 됩니까? 전역 범위 .
    1. 따라서 의해 폐쇄i 되지 않습니다 .f
    2. 따라서 의해 폐쇄setTimeout 되지 않습니다 .f
    3. 따라서 의해 폐쇄console 되지 않습니다 .f

따라서 기능 f은 폐쇄가 아닙니다.

의 경우 g:

  1. 변수를 나열하십시오.
    1. consoleA는 무료 변수.
    2. i2A는 무료 변수.
  2. 각 자유 변수가 바인딩 된 상위 범위를 찾으십시오.
    1. console되는 바인딩 전역에.
    2. i2되는 바인딩 의 범위에 f.
  3. 어떤 범위에서 함수가 참조 됩니까? 의 범위setTimeout .
    1. 따라서 의해 폐쇄console 되지 않습니다 .g
    2. 그러므로 i2되어 닫혀 의해 g.

따라서 함수는 g자유 변수에 대한 폐쇄입니다 i2(대한 upvalue이다 g) 경우 가있어 참조 내에서가 setTimeout.

당신에게 나쁜 점 : 친구가 폐쇄를 사용하고 있습니다. 내부 기능은 폐쇄입니다.

사례 2 : 프로그램

for (var i = 0; i < 10; i++) {
    setTimeout((function f(i2) {
        return function g() {
            console.log(i2);
        };
    })(i), 1000);
}

위의 프로그램에서이 두 가지 기능은 다음과 같습니다 fg. 그들이 폐쇄인지 보자.

의 경우 f:

  1. 변수를 나열하십시오.
    1. i2A는 로컬 변수.
    2. gA는 로컬 변수.
    3. consoleA는 무료 변수.
  2. 각 자유 변수가 바인딩 된 상위 범위를 찾으십시오.
    1. console되는 바인딩 전역에.
  3. 어떤 범위에서 함수가 참조 됩니까? 전역 범위 .
    1. 따라서 의해 폐쇄console 되지 않습니다 .f

따라서 기능 f은 폐쇄가 아닙니다.

의 경우 g:

  1. 변수를 나열하십시오.
    1. consoleA는 무료 변수.
    2. i2A는 무료 변수.
  2. 각 자유 변수가 바인딩 된 상위 범위를 찾으십시오.
    1. console되는 바인딩 전역에.
    2. i2되는 바인딩 의 범위에 f.
  3. 어떤 범위에서 함수가 참조 됩니까? 의 범위setTimeout .
    1. 따라서 의해 폐쇄console 되지 않습니다 .g
    2. 그러므로 i2되어 닫혀 의해 g.

따라서 함수는 g자유 변수에 대한 폐쇄입니다 i2(대한 upvalue이다 g) 경우 가있어 참조 내에서가 setTimeout.

당신을 위해 좋은 : 당신은 폐쇄를 사용하고 있습니다. 내부 기능은 폐쇄입니다.

그래서 당신과 당신의 친구 모두 폐쇄를 사용하고 있습니다. 말다툼을 멈추십시오. 클로저의 개념과 두 사람 모두를 식별하는 방법을 명확하게 정리했으면합니다.

편집 : 모든 함수가 왜 폐쇄되는지에 대한 간단한 설명 (크레딧 @ 피터) :

먼저 다음 프로그램을 고려해 봅시다 ( 제어입니다 ).

lexicalScope();

function lexicalScope() {
    var message = "This is the control. You should be able to see this message being alerted.";

    regularFunction();

    function regularFunction() {
        alert(eval("message"));
    }
}

  1. 우리는 모두 알고 lexicalScoperegularFunction폐쇄하지 않습니다 위의 정의에서 .
  2. 프로그램을 실행할 때 클로저가 아니기 때문에 경고를받을 것으로 예상 message 됩니다 (예 : 부모 범위의 모든 변수에 액세스 할 수 있음 ). regularFunctionmessage
  3. 우리가 프로그램을 실행할 때 실제로 경고 되는 것을 관찰 합니다 message.

다음 프로그램을 고려해 봅시다 ( 대안입니다 ).

var closureFunction = lexicalScope();

closureFunction();

function lexicalScope() {
    var message = "This is the alternative. If you see this message being alerted then in means that every function in JavaScript is a closure.";

    return function closureFunction() {
        alert(eval("message"));
    };
}

  1. 우리는 단지 위의 정의에서closureFunction 폐쇄 것임을 알고 있습니다.
  2. 우리가 프로그램을 실행하면 우리는 기대 message 경고를 할 수 없습니다 때문에 closureFunction (즉, 그것은 단지 모든에 액세스 할 수있는 폐쇄입니다 비 지역 변수함수가 작성 될 때 ( 이 답변을 참조 -이 포함되지 않습니다이) message).
  3. 프로그램을 실행할 때 실제로 경고 하고 있음 을 관찰 합니다 message.

우리는 이것으로부터 무엇을 추론합니까?

  1. JavaScript 인터프리터는 클로저를 다른 함수를 처리하는 방식과 다르게 취급하지 않습니다.
  2. 모든 기능에는 스코프 체인 이 포함됩니다. 클로저에는 별도의 참조 환경 이 없습니다 .
  3. 클로저는 다른 모든 기능과 같습니다. 우리는 그것들이 그들이 속한 범위 밖의 범위에서 참조 될 때 그것들을 클로저라고 부릅니다 . 이것은 흥미로운 경우 이기 때문 입니다.

답변

closure정의 에 따르면 :

“클로저”는 변수 를 묶는 환경 (표현을 “닫는”) 과 함께 자유 변수를 가질 있는 표현식 (일반적으로 함수)입니다 .

당신이 사용하는 closure당신은 함수의 외부에서 정의 된 변수를 사용하는 함수를 정의합니다. (우리는 변수를 자유 변수 라고 부릅니다 ).
그들은 모두 사용합니다 closure(첫 번째 예에서도).


답변

간단히 말해서 자바 스크립트 클로저 로 기능 할 수 액세스를 가변 되는 어휘 – 부모 함수에서 선언을 .

더 자세한 설명을 보자. 클로저를 이해하려면 JavaScript의 변수 범위를 이해하는 것이 중요합니다.

범위

JavaScript 범위는 함수로 정의됩니다. 모든 함수는 새로운 범위를 정의합니다.

다음 예제를 고려하십시오.

function f()
{//begin of scope f
  var foo='hello'; //foo is declared in scope f
  for(var i=0;i<2;i++){//i is declared in scope f
     //the for loop is not a function, therefore we are still in scope f
     var bar = 'Am I accessible?';//bar is declared in scope f
     console.log(foo);
  }
  console.log(i);
  console.log(bar);
}//end of scope f

f 인쇄를 호출

hello
hello
2
Am I Accessible?

이제 g다른 함수 내에 정의 된 함수가있는 경우를 고려해 봅시다 f.

function f()
{//begin of scope f
  function g()
  {//being of scope g
    /*...*/
  }//end of scope g
  /*...*/
}//end of scope f

f어휘 부모 를 호출 합니다 g. 앞에서 설명했듯이 이제 우리는 두 가지 범위를 갖습니다. 범위 f와 범위g .

그러나 한 범위는 다른 범위 내에 “내”있으며, 하위 함수의 범위는 부모 함수 범위의 일부입니까? 부모 함수의 범위에서 선언 된 변수는 어떻게됩니까? 하위 기능의 범위에서 액세스 할 수 있습니까? 바로 폐쇄가 시작되는 곳입니다.

폐쇄

JavaScript에서 함수 g는 범위에 선언 된 변수 g뿐만 아니라 상위 함수 범위에 선언 된 변수에도 액세스 할 수 있습니다.f .

다음을 고려하십시오.

function f()//lexical parent function
{//begin of scope f
  var foo='hello'; //foo declared in scope f
  function g()
  {//being of scope g
    var bar='bla'; //bar declared in scope g
    console.log(foo);
  }//end of scope g
  g();
  console.log(bar);
}//end of scope f

f 인쇄를 호출

hello
undefined

라인을 보자 console.log(foo);. 이 시점에서 우리는 범위 내에 있으며 scope 에 선언 된 g변수에 액세스하려고합니다 . 그러나 앞에서 언급했듯이 여기서는 어휘 부모 함수에 선언 된 변수에 액세스 할 수 있습니다. 의 어휘 부모입니다 . 따라서 인쇄됩니다.
이제 라인을 보자 . 이 시점에서 우리는 범위 내에 있으며 scope 에 선언 된 변수에 액세스하려고합니다 . 현재 범위에서 선언되지 않았으며 함수 가의 부모가 아니므 로 정의되지 않았습니다.foofgfhello
console.log(bar);fbargbargfbar

실제로 우리는 어휘 “grand parent”함수의 범위에서 선언 된 변수에 액세스 할 수도 있습니다. 따라서 함수 h내에 정의 된 함수가 있다면g

function f()
{//begin of scope f
  function g()
  {//being of scope g
    function h()
    {//being of scope h
      /*...*/
    }//end of scope h
    /*...*/
  }//end of scope g
  /*...*/
}//end of scope f

다음 h함수의 범위에 선언 된 모든 변수에 액세스 할 수있을 것 h, g등을 f. 이것은 클로저 로 이루어집니다 . JavaScript 클로저 에서는 어휘 부모 함수, 어휘 그랜드 부모 함수, 어휘 그랜드 부모 함수 등에서 선언 된 모든 변수에 액세스 할 수 있습니다. 이는 범위 체인 으로 볼 수 있습니다 . scope of current function -> scope of lexical parent function -> scope of lexical grand parent function -> ... 어휘 부모가없는 마지막 부모 함수까지

윈도우 객체

실제로 체인은 마지막 부모 함수에서 멈추지 않습니다. 특별한 범위가 하나 더 있습니다. 전역 범위 . 함수에서 선언되지 않은 모든 변수는 전역 범위에서 선언 된 것으로 간주됩니다. 글로벌 스코프에는 두 가지 특성이 있습니다.

  • 전역 범위에서 선언 된 모든 변수는 모든 곳에서 액세스 가능
  • 전역 범위에서 선언 된 변수는 window객체 의 속성에 해당 합니다.

따라서 foo전역 범위에서 변수를 선언하는 두 가지 방법이 있습니다 . 함수에서 선언하지 않거나 foo윈도우 객체 의 속성 을 설정하여 .

두 시도 모두 클로저를 사용합니다.

더 자세한 설명을 읽었으므로 이제 두 솔루션 모두 클로저를 사용하는 것이 분명 할 수 있습니다. 그러나 확실하게 증거를 만들어 봅시다.

새로운 프로그래밍 언어를 만들어 보자. JavaScript-No-Closure. 이름에서 알 수 있듯이 JavaScript-No-Closure는 클로저를 지원하지 않는다는 점을 제외하면 JavaScript와 동일합니다.

다시 말해;

var foo = 'hello';
function f(){console.log(foo)};
f();
//JavaScript-No-Closure prints undefined
//JavaSript prints hello

자, JavaScript-No-Closure를 사용하는 첫 번째 솔루션에서 어떤 일이 발생하는지 봅시다.

for(var i = 0; i < 10; i++) {
  (function(){
    var i2 = i;
    setTimeout(function(){
        console.log(i2); //i2 is undefined in JavaScript-No-Closure 
    }, 1000)
  })();
}

따라서 이것은 인쇄됩니다 undefined JavaScript-No-Closure에서 10 번 .

따라서 첫 번째 솔루션은 클로저를 사용합니다.

두 번째 해결책을 보자.

for(var i = 0; i < 10; i++) {
  setTimeout((function(i2){
    return function() {
        console.log(i2); //i2 is undefined in JavaScript-No-Closure
    }
  })(i), 1000);
}

따라서 이것은 인쇄됩니다 undefined JavaScript-No-Closure에서 10 번 .

두 솔루션 모두 클로저를 사용합니다.

편집 :이 3 개의 코드 스 니펫이 전역 범위에 정의되어 있지 않은 것으로 가정합니다. 그렇지 않으면 변수 foo와 객체가 객체에 i바인딩 window되므로 windowJavaScript와 JavaScript-No-Closure 의 객체를 통해 액세스 할 수 있습니다 .


답변

나는 누군가가 이것을 설명하는 방식에 결코 행복하지 않았습니다.

클로저를 이해하는 열쇠는 클로저가없는 JS의 모습을 이해하는 것입니다.

클로저가 없으면 오류가 발생합니다.

function outerFunc(){
    var outerVar = 'an outerFunc var';
    return function(){
        alert(outerVar);
    }
}

outerFunc()(); //returns inner function and fires it

outerFunc가 가상의 클로저 비활성화 버전의 JavaScript로 반환되면 outerVar에 대한 참조는 가비지 수집되어 내부 함수가 참조 할 수있는 부분이 없어집니다.

클로저는 본질적으로 내부 함수가 외부 함수의 변수를 참조 할 때 해당 변수가 존재할 수 있도록하는 특수 규칙입니다. 클로저를 사용하면 외부 함수가 수행되거나 포인트를 기억하는 데 도움이되는 경우 ‘닫힌’후에도 참조 된 var가 유지됩니다.

클로저를 사용하더라도 로컬을 참조하는 내부 펑크가없는 함수에서 로컬 var의 수명주기는 클로저리스 버전에서와 동일하게 작동합니다. 함수가 완료되면 지역 주민은 가비지 수집을받습니다.

내부 펑크에서 외부 var에 대한 참조가 있으면 도어 잼이 참조 var에 대한 가비지 수집 방식과 유사합니다.

클로저를 보는 더 정확한 방법은 내부 함수가 기본적으로 내부 범위를 자체 범위 소리로 사용한다는 것입니다.

그러나 참조 된 컨텍스트는 사실 스냅 샷과는 달리 영구적입니다. 외부 함수의 로컬 변수를 계속 증가시키고 로깅하는 반환 된 내부 함수를 반복적으로 실행하면 더 높은 값을 경고합니다.

function outerFunc(){
    var incrementMe = 0;
    return function(){ incrementMe++; console.log(incrementMe); }
}
var inc = outerFunc();
inc(); //logs 1
inc(); //logs 2


답변

둘 다 클로저를 사용하고 있습니다.

나는 여기 Wikipedia 정의 와 함께 갈 것이다.

컴퓨터 과학에서 클로저 (어휘 폐쇄 또는 함수 클로저)는 참조 환경과 함께 함수에 대한 함수 또는 참조입니다. 해당 함수의 로컬 변수가 아닌 각 변수 (자유 변수라고도 함)에 대한 참조를 저장하는 테이블 . 일반 함수 포인터와 달리 클로저는 함수가 즉각적인 어휘 범위를 벗어난 경우에도 로컬이 아닌 변수에 액세스 할 수 있도록합니다.

친구의 시도 i는 값을 가져 와서 로컬에 저장할 사본을 만들어 로컬이 아닌 변수를 명확하게 사용합니다 i2.

자신의 시도는 i(콜 사이트의 범위에있는) 익명 함수에 인수로 전달합니다. 이것은 지금까지의 클로저가 아니지만 해당 함수는 동일한를 참조하는 다른 함수를 반환합니다 i2. 내부 익명 함수 내부 i2는 로컬이 아니므로 클로저가 생성됩니다.


답변

당신과 당신의 친구는 모두 폐쇄를 사용합니다 :

클로저는 함수와 해당 함수가 작성된 환경의 두 가지를 결합한 특별한 종류의 객체입니다. 환경은 클로저가 생성 될 때 범위 내에 있던 로컬 변수로 구성됩니다.

MDN : https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures

function(){ console.log(i2); }익명 함수의 폐쇄 내부에 정의 된 친구의 코드 함수에서 function(){ var i2 = i; ...로컬 변수를 읽고 쓸 수 있습니다 i2.

코드 함수에서 함수 function(){ console.log(i2); }폐쇄 내부에 정의되어 function(i2){ return ...있으며 로컬 가치를 읽거나 쓸 수 있습니다 i2(이 경우 매개 변수로 선언).

두 경우 모두 함수는 function(){ console.log(i2); }로 전달됩니다 setTimeout.

메모리 사용량이 적은 다른 방법은 다음과 같습니다.

function fGenerator(i2){
    return function(){
        console.log(i2);
    }
}
for(var i = 0; i < 10; i++) {
    setTimeout(fGenerator(i), 1000);
}


답변

폐쇄

클로저는 함수가 아니며 표현식이 아닙니다. 함수 스코프 외부에서 사용 된 변수와 함수 내부에서 사용되는 일종의 ‘스냅 샷’으로 표시되어야합니다. 문법적으로, ‘변수를 닫습니다’라고 말해야합니다.

다시 말하면, 클로저는 함수가 의존하는 관련 변수 컨텍스트의 복사본입니다.

한 번 더 (naïf) : 클로저가 매개 변수로 전달되지 않은 변수에 액세스 할 수 있습니다.

이 기능 개념은 사용하는 프로그래밍 언어 / 환경에 따라 크게 달라집니다. JavaScript에서 클로저는 어휘 범위 (대부분의 c 언어에서 적용됨)에 의존합니다.

따라서 함수를 반환하는 것은 대부분 익명 / 명명되지 않은 함수를 반환합니다. 함수가 변수로 액세스하지 않고 매개 변수로 전달되지 않고 (어휘) 범위 내에서 클로저가 발생했습니다.

따라서 귀하의 예와 관련하여 :

// 1
for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i); // closure, only when loop finishes within 1000 ms,
    }, 1000);           // i = 10 for all functions
}
// 2
for(var i = 0; i < 10; i++) {
    (function(){
        var i2 = i; // closure of i (lexical scope: for-loop)
        setTimeout(function(){
            console.log(i2); // closure of i2 (lexical scope:outer function)
        }, 1000)
    })();
}
// 3
for(var i = 0; i < 10; i++) {
    setTimeout((function(i2){
        return function() {
            console.log(i2); // closure of i2 (outer scope)

        }
    })(i), 1000); // param access i (no closure)
}

모두 폐쇄를 사용하고 있습니다. 실행 지점과 클로저를 혼동하지 마십시오. 클로저의 ‘스냅 샷’을 잘못된 순간에 가져 오면 값이 예상치 못한 것일 수 있지만 확실히 클로저가 발생합니다!