[javascript] Chrome 디버거가 닫힌 로컬 변수가 정의되지 않은 이유는 무엇입니까?

이 코드로 :

function baz() {
  var x = "foo";

  function bar() {
    debugger;
  };
  bar();
}
baz();

이 예기치 않은 결과가 나타납니다.

여기에 이미지 설명을 입력하십시오

코드를 변경하면 :

function baz() {
  var x = "foo";

  function bar() {
    x;
    debugger;
  };
  bar();
}

예상 결과를 얻습니다.

여기에 이미지 설명을 입력하십시오

또한 eval내부 함수 내에 호출이 있으면 원하는대로 변수에 액세스 할 수 있습니다 (전달하는 대상은 중요하지 않습니다 eval).

한편, Firefox 개발 도구는 두 상황 모두에서 예상되는 동작을 제공합니다.

Chrome에서 디버거가 Firefox보다 편리하게 작동하지 않는 것은 무엇입니까? 버전 41.0.2272.43 베타 (64 비트)를 포함하여 한동안이 동작을 관찰했습니다.

Chrome의 자바 스크립트 엔진이 가능한 한 기능을 “평평하게 만드는”것입니까?

I는 제 2 가변 흥미롭게 경우 추가 되어 내부에서 참조를 함수의 x변수는 아직 정의되지 않는다.

대화 형 디버거를 사용할 때 범위와 변수 정의가있는 단점이 있음을 이해하지만 언어 사양에 따라 이러한 단점에 대한 “최상의”솔루션이 있어야합니다. Chrome이 Firefox보다 더 최적화되어 있기 때문에 궁금합니다. 또한 개발 중에 이러한 최적화를 쉽게 비활성화 할 수 있는지 여부 (개발 도구가 열려있을 때 비활성화해야합니까?)

또한 debugger문장 뿐만 아니라 중단 점으로 이것을 재현 할 수 있습니다 .



답변

나는 당신이 요구하는 것에 관한 v8 이슈 보고서 를 발견했습니다 .

이제 해당 이슈 보고서에서 말한 내용을 요약하면 … v8은 함수의 로컬 변수를 스택 또는 힙에있는 “컨텍스트”개체에 저장할 수 있습니다 . 함수에 변수를 참조하는 내부 함수가 포함되어 있지 않으면 스택에 로컬 변수를 할당합니다. 최적화 입니다. 경우 모든 내부 함수는 로컬 변수를 참조,이 변수 (힙 대신 스택 IE) 콘텍스트 객체에 넣어지게된다. 의 경우 eval는 특별합니다. 내부 함수에 의해 전혀 호출되지 않으면 모든 로컬 변수가 컨텍스트 객체에 배치됩니다.

컨텍스트 객체의 이유는 일반적으로 외부 함수에서 내부 함수를 반환 할 수 있으며 외부 함수가 실행되는 동안 존재하는 스택을 더 이상 사용할 수 없기 때문입니다. 따라서 내부 함수가 액세스하는 것은 외부 함수에서 생존하고 스택이 아닌 힙에 있어야합니다.

디버거는 스택에있는 변수를 검사 할 수 없습니다. 디버깅에서 발생하는 문제와 관련하여 한 프로젝트 멤버 다음과 같이 말합니다 .

내가 생각할 수있는 유일한 해결책은 devtools가 켜질 때마다 모든 코드를 선택 해제하고 강제 컨텍스트 할당으로 다시 컴파일한다는 것입니다. devtools를 사용하면 성능이 크게 저하됩니다.

다음은 “내부 함수가 변수를 참조하는 경우 컨텍스트 객체에 넣습니다”의 예입니다. 이것을 실행하면 결코 호출되지 않는 함수 에서만 사용 되지만 명령문 x에서 액세스 할 수 있습니다 !debuggerxfoo

function baz() {
  var x = "x value";
  var z = "z value";

  function foo () {
    console.log(x);
  }

  function bar() {
    debugger;
  };

  bar();
}
baz();


답변

@Louis와 마찬가지로 v8 최적화로 인해 발생했습니다. 이 변수가 표시되는 프레임으로 콜 스택을 통과 할 수 있습니다.

call1
call2

또는 교체 debugger와 함께

eval('debugger');

eval 현재 청크를 해제합니다


답변

나는 또한 nodejs에서 이것을 발견했습니다. 코드가 컴파일 될 때 x안에 bar나타나지 않으면 x의 범위 내에서 사용할 수 없다고 생각합니다 (그리고 나는 단지 추측 일뿐 입니다) bar. 이것은 아마도 조금 더 효율적일 것입니다. 문제는 xin 이없는 경우에도 bar디버거를 실행하여 여전히 x내부 에서 액세스해야한다는 것을 잊어 버린 사람 bar입니다.


답변

와, 정말 재미 있어요!

다른 사람들이 언급했듯이 이것은와 관련이 scope있지만 더 구체적 으로 관련이있는 것 같습니다 debugger scope. 주입 된 스크립트가 개발자 도구에서 평가되면을 결정하는 것 같습니다 ScopeChain. 검사기 / 디버거 범위에 바인딩되어 있기 때문에 약간의 기묘함이 발생합니다. 게시 한 내용의 변형은 다음과 같습니다.

(편집-실제로, 당신은 당신의 원래 질문, 이케, 내 나쁜 점 에서 이것을 언급합니다 ! )

function foo() {
  var x = "bat";
  var y = "man";

  function bar() {
    console.log(x); // logs "bat"

    debugger; // Attempting to access "y" throws the following
              // Uncaught ReferenceError: y is not defined
              // However, x is available in the scopeChain. Weird!
  }
  bar();
}
foo();

야심적이고 호기심이 많은 사람들을 위해 소스를 조사해보십시오.

https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspector
https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/debugger


답변

나는 이것이 변수 및 기능 호이 스팅과 관련이 있다고 생각합니다. JavaScript는 모든 변수 및 함수 선언을 정의 된 함수의 최상위로 가져옵니다. 추가 정보 : http://jamesallardice.com/explaining-function-and-variable-hoisting-in-javascript/

함수에 다른 것이 없기 때문에 Chrome이 범위에서 사용할 수없는 변수로 중단 점을 호출한다고 확신합니다. 이것은 작동하는 것 같습니다 :

function baz() {
  var x = "foo";

  function bar() {
    console.log(x);
    debugger;
  };
  bar();
}

이것처럼 :

function baz() {
  var x = "foo";

  function bar() {
    debugger;
    console.log(x);
  };
  bar();
}

희망, 및 / 또는 위의 링크가 도움이됩니다. 이들은 내가 좋아하는 SO 질문 중 하나입니다, BTW 🙂


답변