Com este código:
function baz() {
var x = "foo";
function bar() {
debugger;
};
bar();
}
baz();
Eu recebo este resultado inesperado:
Quando eu mudo o código:
function baz() {
var x = "foo";
function bar() {
x;
debugger;
};
bar();
}
Eu recebo o resultado esperado:
Além disso, se houver alguma chamada para eval
dentro da função interna, eu posso acessar minha variável como desejo (não importa para o que eu passo eval
).
Enquanto isso, as ferramentas de desenvolvimento do Firefox fornecem o comportamento esperado nas duas circunstâncias.
O que há com o Chrome que o depurador se comporte menos do que o Firefox? Eu observo esse comportamento há algum tempo, incluindo a versão 41.0.2272.43 beta (64 bits).
Será que o mecanismo javascript do Chrome "nivela" as funções quando pode?
Curiosamente, se eu adicionar uma segunda variável que é referenciada na função interna, a x
variável ainda está indefinida.
Entendo que muitas vezes existem peculiaridades com escopo e definição de variável ao usar um depurador interativo, mas parece-me que, com base na especificação da linguagem, deve haver uma "melhor" solução para essas peculiaridades. Portanto, estou muito curioso se isso se deve à otimização do Chrome para além do Firefox. E também se essas otimizações podem ou não ser facilmente desabilitadas durante o desenvolvimento (talvez elas devam ser desabilitadas quando as ferramentas de desenvolvimento estão abertas?).
Além disso, eu posso reproduzir isso com pontos de interrupção e com a debugger
declaração.
fonte
debugger;
linha não é realmente chamada de dentrobar
. Portanto, observe o rastreamento de pilha quando ele pausa no depurador: abar
função é mencionada no stacktrace? Se eu estiver certo, o stacktrace deve dizer que está pausado na linha 5, na linha 7, na linha 9.temp1
é anexada ao console e você pode usá-la para acessar a entrada do escopo.Respostas:
Encontrei um relatório de problema da v8 que é exatamente sobre o que você está perguntando.
Agora, para resumir o que é dito nesse relatório de problema ... a v8 pode armazenar as variáveis locais para uma função na pilha ou em um objeto de "contexto" que fica na pilha. Ele alocará variáveis locais na pilha, desde que a função não contenha nenhuma função interna que se refira a elas. É uma otimização . Se qualquer função interna se referir a uma variável local, essa variável será colocada em um objeto de contexto (ou seja, na pilha em vez de na pilha). O caso de
eval
é especial: se é chamado de alguma forma por uma função interna, todas as variáveis locais são colocadas no objeto de contexto.A razão para o objeto de contexto é que, em geral, você pode retornar uma função interna da externa e, em seguida, a pilha que existia enquanto a função externa era executada não estará mais disponível. Portanto, qualquer coisa acessada pela função interna precisa sobreviver à função externa e viver na pilha, e não na pilha.
O depurador não pode inspecionar as variáveis que estão na pilha. Com relação ao problema encontrado na depuração, um Membro do Projeto diz :
Aqui está um exemplo do "se alguma função interna se refere à variável, coloque-a em um objeto de contexto". Se você executar isso, poderá acessar
x
adebugger
instrução mesmo quex
seja usada apenas nafoo
função, que nunca é chamada !fonte
Como o @Louis disse, causado por otimizações da v8. Você pode atravessar a pilha de chamadas para o quadro em que essa variável está visível:
Ou substitua
debugger
poreval
vai adiar pedaço atualfonte
debugger
e o contexto está realmente disponível. Se você aumentar a pilha um nível para o código que está realmente tentando depurar, voltará a não ter acesso ao contexto. Portanto, é um pouco desajeitado, não sendo possível ver o código que você está depurando enquanto acessa variáveis de fechamento ocultas. Eu vou votar, no entanto, uma vez que me impede de adicionar código que não é obviamente para depuração, e me dá acesso a todo o contexto sem desativar o aplicativo todo.eval
janela de código-fonte amarela para obter acesso ao contexto: você não pode percorrer o código (a menos que coloqueeval('debugger')
entre todas as linhas que deseja percorrer.)controllers.forEach(c => c.update())
e atingi um ponto de interrupção em algum lugar no fundoc.update()
. Se eu selecionar o quadro em quecontrollers.forEach()
é chamado,controllers
será indefinido (mas tudo o mais nesse quadro estará visível). Não consegui reproduzir com uma versão mínima, suponho que possa haver algum limite de complexidade que precise ser passado ou algo assim.somewhere deep inside c.update()
o seu código vai assíncrona e você vê quadro de pilha assíncronaEu também notei isso no nodejs. Acredito (e admito que isso é apenas um palpite) que, quando o código é compilado, se
x
não aparecer dentrobar
, ele não ficax
disponível dentro do escopo debar
. Isso provavelmente o torna um pouco mais eficiente; o problema é que alguém esqueceu (ou não se importava) que, mesmo se não há éx
embar
, você pode optar por executar o depurador e, portanto, ainda precisa de acessox
a partir de dentrobar
.fonte
eval
comando não relacionado . Se a variável for declarada, ela deverá estar acessível.Uau, muito interessante!
Como outros já mencionaram, isso parece estar relacionado
scope
, mas mais especificamente, adebugger scope
. Quando o script injetado é avaliado nas ferramentas do desenvolvedor, parece determinar aScopeChain
, o que resulta em alguma peculiaridade (já que está vinculado ao escopo do inspetor / depurador). Uma variação do que você postou é o seguinte:(EDIT - na verdade, você mencionou isso na sua pergunta original, caramba, meu mal! )
Para os ambiciosos e / ou curiosos, escopo (heh) a fonte para ver o que está acontecendo:
https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspector https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/debugger
fonte
Suspeito que isso tenha a ver com o levantamento de variáveis e funções. JavaScript traz todas as declarações de variáveis e funções para o topo da função em que estão definidas. Mais informações aqui: http://jamesallardice.com/explaining-function-and-variable-hoisting-in-javascript/
Aposto que o Chrome está chamando o ponto de interrupção com a variável indisponível para o escopo, porque não há mais nada na função. Isso parece funcionar:
Como faz isso:
Espero que isso e / ou o link acima ajude. Este é o meu tipo favorito de perguntas sobre SO, BTW :)
fonte