developer tip

루프에서 JavaScript 클로저의 사용을 설명하십시오.

optionbox 2021. 1. 6. 08:03
반응형

루프에서 JavaScript 클로저의 사용을 설명하십시오.


이 질문에 이미 답변이 있습니다.

루프 내부의 클로저와 클로저에 대한 여러 설명을 읽었습니다. 개념을 이해하기가 어렵습니다. 이 코드가 있습니다. 코드를 최대한 줄여 클로저 개념을 더 명확하게 할 수있는 방법이 있습니까? i두 괄호 안에 있는 부분을 이해하는 데 어려움을 겪고 있습니다. 감사

function addLinks () {
    for (var i=0, link; i<5; i++) {

        link = document.createElement("a");
        link.innerHTML = "Link " + i;


        link.onclick = function (num) {
            return function () {
                alert(num);
            };
        }(i);
        document.body.appendChild(link);

    }
}
window.onload = addLinks;

경고 : 긴 답변

이것은 내부 회사 위키에 작성한 기사에서 직접 복사 한 것입니다.

질문 : 루프에서 클로저를 올바르게 사용하는 방법은 무엇입니까? 빠른 답변 : 기능 팩토리를 사용하십시오.

  for (var i=0; i<10; i++) {
    document.getElementById(i).onclick = (function(x){
      return function(){
        alert(x);
      }
    })(i);
  }

또는 더 쉽게 읽을 수있는 버전 :

  function generateMyHandler (x) {
    return function(){
      alert(x);
    }
  }

  for (var i=0; i<10; i++) {
    document.getElementById(i).onclick = generateMyHandler(i);
  }

이것은 종종 자바 스크립트 나 함수형 프로그래밍을 처음 접하는 사람들을 혼란스럽게합니다. 폐쇄가 무엇인지 오해 한 결과입니다.

클로저는 단순히 변수의 값이나 변수에 대한 참조를 전달하지 않습니다. 클로저는 변수 자체를 포착합니다! 다음 코드는이를 설명합니다.

  var message = 'Hello!';
  document.getElementById('foo').onclick = function(){alert(message)};
  message = 'Goodbye!';

'foo'요소를 클릭하면 "Goodbye!"라는 메시지가있는 경고 상자가 생성됩니다. 이 때문에 루프에서 간단한 클로저를 사용하면 모든 클로저가 동일한 변수를 공유하게되며 해당 변수에는 루프에서 할당 된 마지막 값이 포함됩니다. 예를 들면 :

  for (var i=0; i<10; i++) {
    document.getElementById('something'+i).onclick = function(){alert(i)};
  }

클릭하면 모든 요소는 숫자 10의 경고 상자를 생성합니다. 실제로 지금 수행하면 i="hello";모든 요소가 이제 "hello"경고를 생성합니다! 변수 i는 현재 함수 / 범위 / 컨텍스트와 함께 10 개의 함수에서 공유됩니다. 관련된 함수 만 볼 수있는 일종의 개인 전역 변수라고 생각하십시오.

우리가 원하는 것은 해당 변수의 인스턴스 또는 최소한 변수 자체 대신 변수에 대한 간단한 참조입니다. 다행히 자바 스크립트는 이미 참조 (객체) 또는 값 (문자열과 숫자)을 전달하는 메커니즘을 가지고 있습니다. 함수 인수!

함수가 자바 스크립트에서 호출 될 때 해당 함수에 대한 인수는 객체 인 경우 참조로 전달되고 문자열 또는 숫자 인 경우 값으로 전달됩니다. 이것은 클로저에서 변수 공유를 깨기에 충분합니다.

그래서:

  for (var i=0; i<10; i++) {
    document.getElementById(i).onclick =
      (function(x){ /* we use this function expression simply as a factory
                       to return the function we really want to use: */

        /* we want to return a function reference
           so we write a function expression*/
        return function(){
          alert(x); /* x here refers to the argument of the factory function
                       captured by the 'inner' closure */
        }

      /* The brace operators (..) evaluates an expression, in this case this
         function expression which yields a function reference. */

      })(i) /* The function reference generated is then immediately called()
               where the variable i is passed */
  }

I've been programming in JavaScript for a long time, and "closure in a loop" is a very broad topic. I assume you are talking about the practice of using (function(param) { return function(){ ... }; })(param); inside of a for loop in order to preserve the "current value" of the loop when that inner function later executes...

The code:

for(var i=0; i<4; i++) {
  setTimeout(
    // argument #1 to setTimeout is a function.
    // this "outer function" is immediately executed, with `i` as its parameter
    (function(x) {
      // the "outer function" returns an "inner function" which now has x=i at the
      // time the "outer function" was called
      return function() {  
        console.log("i=="+i+", x=="+x);
      };
    })(i) // execute the "closure" immediately, x=i, returns a "callback" function
  // finishing up arguments to setTimeout
  , i*100);
}

Output:

i==4, x==0
i==4, x==1
i==4, x==2
i==4, x==3

As you can see by the output, all of the inner callback functions all point to the same i, however, since each had its own 'closure', the value of x is actually stored as whatever i was at the time of the outer function's execution.

Commonly when you see this pattern, you would use the same variable name as the parameter and the argument to the outer function: (function(i){ })(i) for instance. Any code inside that function (even if executed later, like a callback function) is going to refer to i at the time you called the "outer function".


Well, the "problem" with closures in such a case is, that any access to i would reference the same variable. That is because of ECMA-/Javascripts function scope or lexical scope.

So to avoid that every call to alert(i); would display a 5 (because after the loop finished i === 5), you need to create a new function which invokes itself at runtime.

To achieve this, you need to create a new function, plus you need the extra paranthesis at the end, to invoke the outer function immediately, so link.onclick has now the returned function as reference.


A closure is a construct in which you reference a variable outside the scope in which it's defined. You usually talk about closures in the context of a function.

var helloFunction;
var finished = false;

while (!finished) {
 var message = 'Hello, World!';
 helloFunction = function() {
   alert(message);
 }
 finished = true;
}

helloFunction();

Here, I define the variable message, and define a function that references message. When I define the function to use message, I am creating a closure. This means helloFunction holds a reference to message, so that I can continue to use message, even outside of the scope (the loop body) where message is defined.

Addendum

The (i) in parenthesis is a function call. What's happening is:

  1. You define some function(num) {}. This is called an anonymous function, because it's defined inline and doesn't have a name.
  2. function(num) takes an integer argument, and returns a reference to another function, which is defined as alert(num)
  3. The outer anonymous function is immediately called, with the argument i. So num=i. The result of this call is a function which will do alert(i).
  4. The end result is more or less equivalent to: link.onclick = function() { alert(i); };

To answer the last part of your questions. The two parenthesis invoke the function as any other functions. Why you do it here is that you want to keep what the variable "i" is just at that time. So what it does is, invoke the function, the i is sent as a argument "num". Since it's invoke it will remember the value nume in variable links own scoop.

If you did't to this all link click would result in an alert saying "5"

John Resig, founder of jQuery, has a really nice online presentation explaining this. http://ejohn.org/apps/learn/

..fredrik

ReferenceURL : https://stackoverflow.com/questions/3572480/please-explain-the-use-of-javascript-closures-in-loops

반응형