성가신 폐쇄

2898 단어 javascriptclosure
오늘 저는 처음에 저를 혼란스럽게 하는 행동을 하는 코드를 발견했습니다. 코드에는 루프 내부의 HTML 요소에 onclick 함수를 첨부하는 작업이 포함되었습니다. 이 같은:

let divs = document.getElementsByTagName( "div" );

for ( var i = 0; i < divs.length; i++ )
{
    divs[i].onclick = function () { alert(i); }
}


이 코드에는 클로저와 관련된 미묘한 문제가 있습니다. 우리는 div를 클릭할 때 div의 인덱스를 볼 것으로 예상합니다. 대신 클릭하는 div에 관계없이 총 div 수가 표시됩니다.

익명의 onclick 함수가 생성되면 상위 범위의 변수에 액세스할 수 있습니다. 그래서 변수i에 액세스할 수 있습니다(원하는 방식으로 작동하지 않더라도). 이렇게 하면 onclick 함수의 변수i를 외부 범위(이 경우 전역 범위, 하지만 다른 함수일 수 있음)의 변수i에 바인딩하는 클로저가 생성됩니다.
var 를 사용하여 변수를 생성할 때 해당 범위는 일반적으로 변수가 생성된 함수(또는 전역 범위)가 됩니다. 현대 JS에서는 let 를 사용하여 변수를 생성할 수도 있습니다. 이는 다소 더 복잡하게 작동합니다.
let로 생성된 변수는 가장 가까운 내부 블록 범위에 바인딩됩니다. 이것은 함수, if 문, 루프일 수 있습니다. 중괄호가 사용되는 거의 모든 곳에서. C 유형 언어에 익숙하다면 이 범위 지정 동작이 매우 친숙하게 느껴질 것입니다.

따라서 한 가지 해결책은 varlet 로 변경하는 것입니다. 클로저가 생성되면 함수 범위 변수가 아니라 블록 범위 변수에 바인딩됩니다. 내가 아는 한 루프는 본문이 실행될 때마다 새로운 블록 범위를 생성합니다. 이 컨텍스트에서 클로저는 우리가 원하는 값에 바인딩됩니다.

let divs = document.getElementsByTagName( "div" );

for ( var i = 0; i < divs.length; i++ )
{
    divs[i].onclick = function () { alert(i); }
}


이 질문을 조사하는 동안 MDN article on Closures 을 발견했습니다. "루프에서 클로저 생성: 일반적인 실수"섹션에서 이 문제에 대해 설명합니다.

당신이 나와 같다면 그 문제에 대한 다른 해결책이 무엇인지 궁금할 것입니다. let 키워드는 몇 년 동안 우리와 함께했습니다. MDN 기사에는 몇 가지 다른 솔루션이 나열되어 있습니다. 두 가지 솔루션이 중개 기능을 도입합니다. 내 단순화된 예에서는 다음을 수행할 수 있습니다.

let divs = document.getElementsByTagName( "div" );

for ( var i = 0; i < divs.length; i++ )
{
    (function () {
        var index = i;
        divs[i].onclick = function () { alert(index); }
    })();
}


또는 더 읽기 쉽게 루프 본문 함수에 이름을 지정하고 루프 외부로 이동한 다음 호출하여 i를 인수로 전달할 수 있습니다.

MDN 기사에서는 바인딩할 중간 범위를 생성하여 작동하는 for-loop 자체 대신 forEach를 사용하는 것도 언급합니다.

MDN 기사에서 논의하지 않은 다른 두 가지 접근 방식을 언급하는 것으로 마무리하겠습니다.

1) onclick 함수가 연결된 요소에 속성을 추가하고 이 속성을 참조할 수 있습니다. 예를 들어:

let divs = document.getElementsByTagName( "div" );

for ( var i = 0; i < divs.length; i++ )
{
    divs[i].index = i;
    divs[i].onclick = function () { alert(this.index); }
}

2) 바인드를 사용할 수 있습니다.

let divs = document.getElementsByTagName( "div" );

for ( var i = 0; i < divs.length; i++ )
{
    divs[i].onclick = (function (index) { alert(index); }).bind( divs[i], i );
}

위의 예에서 onclick 함수가 연결되는 요소는 첫 번째 매개변수로 bind에 전달됩니다. 이렇게 하면 원하는 경우 함수this에 액세스할 수 있습니다.

좋은 웹페이지 즐겨찾기