Esse fenômeno é conhecido como: JavaScript Variable Hoisting .
Em nenhum momento você acessa a variável global em sua função; você está sempre acessando a value
variável local .
Seu código é equivalente ao seguinte:
var value = 10;
function test() {
var value;
console.log(value);
value = 20;
console.log(value);
}
test();
Ainda está surpreso com o que você está recebendo undefined
?
Explicação:
Isso é algo que todo programador de JavaScript se depara mais cedo ou mais tarde. Simplificando, quaisquer variáveis que você declarar são sempre içadas para o topo de seu fechamento local. Portanto, mesmo que você tenha declarado sua variável após a primeira console.log
chamada, ela ainda é considerada como se você a tivesse declarado antes disso.
No entanto, apenas a parte da declaração está sendo içada; a atribuição, por outro lado, não é.
Portanto, quando você chamou pela primeira vez console.log(value)
, estava referenciando sua variável declarada localmente, que ainda não tem nada atribuído a ela; daí undefined
.
Aqui está outro exemplo :
var test = 'start';
function end() {
test = 'end';
var test = 'local';
}
end();
alert(test);
O que você acha que isso alertará? Não, não apenas continue lendo, pense sobre isso. Qual é o valor de test
?
Se você disse qualquer coisa diferente start
, você estava errado. O código acima é equivalente a este:
var test = 'start';
function end() {
var test;
test = 'end';
test = 'local';
}
end();
alert(test);
para que a variável global nunca seja afetada.
Como você pode ver, não importa onde você coloque sua declaração de variável, ela sempre é içada para o topo de seu fechamento local.
Nota:
Isso também se aplica a funções.
Considere esta parte do código :
test("Won't work!");
test = function(text) { alert(text); }
o que dará a você um erro de referência:
ReferenceError não capturado: teste não definido
Isso confunde muitos desenvolvedores, pois este código funciona bem:
test("Works!");
function test(text) { alert(text); }
A razão para isso, conforme declarado, é porque a peça de atribuição não é içada. Portanto, no primeiro exemplo, quando test("Won't work!")
foi executado, a test
variável já foi declarada, mas ainda não teve a função atribuída a ela.
No segundo exemplo, não estamos usando atribuição de variável. Em vez disso, estamos usando a sintaxe de declaração de função apropriada, o que faz com que a função seja completamente içada.
Ben Cherry escreveu um excelente artigo sobre isso, apropriadamente intitulado JavaScript Scoping and Hoisting .
Leia-o. Ele lhe dará a imagem completa com todos os detalhes.
Fiquei um tanto desapontado com a explicação do problema aqui, mas ninguém propôs uma solução. Se você quiser acessar uma variável global no escopo da função sem que a função crie uma var local indefinida primeiro, faça referência a var como
window.varName
fonte
Variáveis em JavaScript sempre têm escopo de função ampla. Mesmo que tenham sido definidos no meio da função, eles são visíveis antes. Fenômenos semelhantes podem ser observados com o içamento de função.
Dito isso, o primeiro
console.log(value)
vê avalue
variável (a interna que sombreia a externavalue
), mas ela ainda não foi inicializada. Você pode pensar nisso como se todas as declarações de variáveis fossem movidas implicitamente para o início da função ( não o bloco de código mais interno), enquanto as definições são deixadas no mesmo lugar.Veja também
fonte
Existe uma variável global
value
, mas quando o controle entra natest
função, outravalue
variável é declarada, o que obscurece a global. Uma vez que as declarações de variáveis ( mas não as atribuições ) em JavaScript são içadas ao topo do escopo em que são declaradas://value == undefined (global) var value = 10; //value == 10 (global) function test() { //value == undefined (local) var value = 20; //value == 20 (local) } //value == 10 (global)
Observe que o mesmo é verdadeiro para as declarações de função, o que significa que você pode chamar uma função antes que ela pareça estar definida em seu código:
test(); //Call the function before it appears in the source function test() { //Do stuff }
Também é importante notar que quando você combina os dois em uma expressão de função, a variável estará
undefined
até que a atribuição ocorra, portanto, você não pode chamar a função até que isso aconteça:var test = function() { //Do stuff }; test(); //Have to call the function after the assignment
fonte
A maneira mais simples de manter o acesso a variáveis externas (não apenas de escopo global) é, obviamente, tentar não redeclará-las sob o mesmo nome em funções; só não use var lá. Recomenda-se o uso de regras de nomenclatura descritivas adequadas . Com eles, será difícil acabar com variáveis nomeadas como valor (este aspecto não está necessariamente relacionado ao exemplo na questão, pois o nome da variável pode ter sido fornecido para simplificar).
Se a função puder ser reutilizada em outro lugar e, portanto, não houver garantia de que a variável externa realmente definida nesse novo contexto, a função Eval pode ser usada. É lento nesta operação, por isso não é recomendado para funções que exigem desempenho:
if (typeof variable === "undefined") { eval("var variable = 'Some value';"); }
Se a variável de escopo externo que você deseja acessar é definida em uma função nomeada, então ela pode ser anexada à própria função em primeiro lugar e então acessada de qualquer lugar no código - seja de funções profundamente aninhadas ou manipuladores de eventos fora de todo o resto. Observe que o acesso às propriedades é muito mais lento e exigiria que você alterasse a forma como programa, por isso não é recomendado a menos que seja realmente necessário: Variáveis como propriedades de funções (JSFiddle) :
// (the wrapper-binder is only necessary for using variables-properties // via "this"instead of the function's name) var functionAsImplicitObjectBody = function() { function someNestedFunction() { var redefinableVariable = "redefinableVariable's value from someNestedFunction"; console.log('--> functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty); console.log('--> redefinableVariable: ', redefinableVariable); } var redefinableVariable = "redefinableVariable's value from someFunctionBody"; console.log('this.variableAsProperty: ', this.variableAsProperty); console.log('functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty); console.log('redefinableVariable: ', redefinableVariable); someNestedFunction(); }, functionAsImplicitObject = functionAsImplicitObjectBody.bind(functionAsImplicitObjectBody); functionAsImplicitObjectBody.variableAsProperty = "variableAsProperty's value, set at time stamp: " + (new Date()).getTime(); functionAsImplicitObject(); // (spread-like operator "..." provides passing of any number of arguments to // the target internal "func" function in as many steps as necessary) var functionAsExplicitObject = function(...arguments) { var functionAsExplicitObjectBody = { variableAsProperty: "variableAsProperty's value", func: function(argument1, argument2) { function someNestedFunction() { console.log('--> functionAsExplicitObjectBody.variableAsProperty: ', functionAsExplicitObjectBody.variableAsProperty); } console.log("argument1: ", argument1); console.log("argument2: ", argument2); console.log("this.variableAsProperty: ", this.variableAsProperty); someNestedFunction(); } }; return functionAsExplicitObjectBody.func(...arguments); }; functionAsExplicitObject("argument1's value", "argument2's value");
fonte
Eu estava tendo o mesmo problema, mesmo com variáveis globais. Meu problema, descobri, era a variável global não persistir entre os arquivos html.
<script> window.myVar = 'foo'; window.myVarTwo = 'bar'; </script> <object type="text/html" data="/myDataSource.html"></object>
Tentei fazer referência a myVar e myVarTwo no arquivo HTML carregado, mas recebi o erro indefinido. Longa história / resumindo o dia, descobri que poderia fazer referência às variáveis usando:
<!DOCTYPE html> <html lang="en"> <!! other stuff here !!> <script> var myHTMLVar = this.parent.myVar /* other stuff here */ </script> </html>
fonte