2010-02-19 8 views
3

Javascript로 둘러보고 (읽는 법 : learning) 주변을 둘러 보았고, 매우 이해하기 어려운 것 같습니다. 브라우저에 대한 중요성을 '느슨하게하는'것으로 보이는 폐쇄 및 참조와 관련이 있습니다.Javascript, 클로저의 이상한 행동

사용중인 브라우저는 입니다. Chromium 5.0.307.7입니다.

어쨌든, 여기에 몇 가지 코드는 다음과 같습니다

여기에 발생하는 첫 번째 인수가 올바른는 HTMLElement에 기록됩니다 것입니다 있지만, 모든 사람이 후하지 않습니다 무엇
HTMLElement.prototype.writeInSteps = function() { 
    var i = 0; 
    var elem = this; 
    var args = arguments; 

    function step() { 
    elem.innerHTML += args[i]; 

    if(i < args.length) { 
     i += 1; 
    } else { 
     elem.innerHTML = ""; 
     i = 0; 
    } 


    setTimeout(step, 500); 
    } 

    step(); 
} 

. 첫 번째 인수 다음에 'elem'이 참조하는 다른 요소에 다음 인수가 기록됩니다. 나는이 함수를 호출 한 후 아무것도 기록 삼가 경우

div.writeInSteps("This", " is", " not", " working"); 
$id("body").innerHTML += "Doh!"; 

, 작동하는 것 같다 :

나는 또한 그것을 언급해야한다, 이것은 단지 다음과 같이이 함수를 직접 호출 한 후 뭔가를 쓸 때 일어날 것 같다 승인. 내가 대신 위의 코드를 변경하는 경우

는 :

HTMLElement.prototype.writeInSteps = function() { 
    var i = 0; 
    var e = this.id; 
    var args = arguments; 

    function step() { 
    var elem = $id(e); 
    elem.innerHTML += args[i]; 

    if(i < args.length) { 
     i += 1; 
    } else { 
     elem.innerHTML = ""; 
     i = 0; 
    } 


    setTimeout(step, 500); 
    } 

    step(); 
} 

모든 멋쟁이입니다. 내 질문은 첫 번째 버전에서 실제로 무슨 일이 벌어지고있는 것입니까?

EDIT : "... 직접 작성한 후 ..."에 대한 요청 세부 정보 및 ntownsend에서 요청한 브라우저 사용법으로 업데이트되었습니다. Bryan Matthews,이 질문을 지나치게 복잡하게 만들지 않고 테스트 페이지를 제공하는 방법을 잘 모르겠습니다.

+1

수정 된 버전에서 'elem'을 지역 변수로 만들어야합니다. 지금은 함축적 인 전역 변수이며 조심하지 않으면 기묘한 오류를 일으키기 쉽습니다 (왜 필요하지 않으면 걱정하지 않으시겠습니까?). –

+0

이 동작을 보여주는 샘플 페이지를 제공해 주시겠습니까? 첫 번째 버전을 사용해 보았는데 여러 번 동시에 실행하는 것을 포함하여 잘 작동하는 것 같습니다. –

+0

제프가 옳습니다. 이것은 리팩토링 오류 일뿐입니다. –

답변

3

나는 이것이 JavaScript 문제가 아니라 DOM 문제라고 생각합니다.

제 생각에 뭔가가 단계적으로 쓰려고하는 요소의 조상을 돌연변이 시켰습니다. 예를 들어, 요소의 부모 중 innerHTML이 설정된 경우 (정확히 동일한 문자열로 생각할 수도 있습니다), 요소 참조는 더 이상 DOM에없는 요소에 대한 것입니다. 매번 ID로 요소를 다시 가져 오면 해당 문제가 해결됩니다. 당신이 현재 요소의 step 아니면 HTMLElement.prototype.writeInSteps.step() 호출하고 있는지 -

+0

+1 내 추측도 마찬가지입니다. innerHTML을 설정하면 요소를 다시 만들 때 "최적화"되어 참조를 무효화 할 수 있습니다. – Tomas

+0

'innerHTML'을 설정하면 항상 ** 모든 ** 자손 노드를 파괴하고 마크 업에서 새 노드로 대체하고, 프로세스에서 JavaScript 참조, 속성 및 이벤트 핸들러를 잃어 버릴 수 있습니다. 이것이 ** innerHTML + ='을 사용하여 내용을 추가하지 말아야하는 이유입니다. – bobince

+0

콘텐츠를 추가하려면 어떻게해야합니까? – jimka

-2

내 생각 setTimeout(step, 500); 콜백의 실행이 this 아주 작은 아이디어가있다?

두 번째 코드를 두 개의 다른 요소에서 동시에 시도하십시오 (첫 번째 시간 초과 전에 두 번째 코드 writeInSteps이 오도록). 나는 그것이 당신이 기대하는 바를 꽤하지 않을 것이라고 확신합니다.

물론 제프도 맞을 수 있습니다. innerHTML은 사양에 의해 읽기 전용이며,이 트리에 쓰는 것은 전체 트리를 다시 작성 (또는 모든 참조를 종료) 할 수도 있고하지 않을 수도 있습니다.

+0

그러나 이것이 두 번째 예제가 효과가 있었던 이유를 설명하지는 못합니다. –

+1

아니요,'step'은'writeInSteps'에 대한 현재 호출에서 정의 된 함수를 모호하지 않게 참조합니다.'writeInSteps'가 호출 될 때마다 새로 생성 된 로컬 바인딩입니다. 'step'이'this'를 결코 참조하지 않기 때문에,'setTimeout'에'step'을 직접 전달할 때 문제가 없어야합니다. –

+2

-1 이것은 문제가되지 않습니다. elem closure를 사용하는 전체적인 생각은 이것에 대한 참조를 유지하는 것입니다. – Tomas

0

당신이 (당신의 예에 따라, body 요소) elem의 조상의 innerHTML를 교체하는 경우, 다음 elem가 더 이상 존재하지 않습니다. step이 원래의 elem이 파괴 된 후에 참조 할 경우 elem은 다른 것입니다.

브라우저가 수행해야 할 올바른 작업은 아마도 elem 참조를 제거하는 것이지만, 그렇게하는 것처럼 보이지는 않습니다.