Este fenómeno se conoce como: Elevación variable de JavaScript .
En ningún momento accede a la variable global en su función; solo está accediendo a la value
variable local .
Su código es equivalente a lo siguiente:
var value = 10;
function test() {
var value;
console.log(value);
value = 20;
console.log(value);
}
test();
¿Todavía te sorprende que te estés recibiendo undefined
?
Explicación:
Esto es algo con lo que todos los programadores de JavaScript se topan tarde o temprano. En pocas palabras, lo variables que se declaran siempre está izada a la parte superior de su clausura local. Entonces, aunque declaró su variable después de la primera console.log
llamada, todavía se considera como si la hubiera declarado antes.
Sin embargo, solo se iza la parte de la declaración; la asignación, por otro lado, no lo es.
Entonces, cuando llamó por primera vez console.log(value)
, estaba haciendo referencia a su variable declarada localmente, que aún no tiene nada asignado; por lo tanto undefined
.
Aquí hay otro ejemplo :
var test = 'start';
function end() {
test = 'end';
var test = 'local';
}
end();
alert(test);
¿Qué crees que alertará esto? No, no se limite a seguir leyendo, piénselo. ¿Cuál es el valor de test
?
Si dijiste algo más que start
, estabas equivocado. El código anterior es equivalente a esto:
var test = 'start';
function end() {
var test;
test = 'end';
test = 'local';
}
end();
alert(test);
para que la variable global nunca se vea afectada.
Como se puede ver, no importa donde usted pone su declaración de variables, siempre es izada a la parte superior de su clausura local.
Nota al margen:
Esto también se aplica a las funciones.
Considere este fragmento de código :
test("Won't work!");
test = function(text) { alert(text); }
que le dará un error de referencia:
Error de referencia no detectado: la prueba no está definida
Esto desconcierta a muchos desarrolladores, ya que este código funciona bien:
test("Works!");
function test(text) { alert(text); }
La razón de esto, como se indicó, es que la parte de asignación no está izada. Entonces, en el primer ejemplo, cuando test("Won't work!")
se ejecutó, la test
variable ya ha sido declarada, pero aún no se le ha asignado la función.
En el segundo ejemplo, no usamos la asignación de variables. Más bien, estamos usando la sintaxis de declaración de función adecuada, lo que hace que la función sea completamente mejorada.
Ben Cherry ha escrito un artículo excelente sobre esto, apropiadamente titulado Alcance y elevación de JavaScript .
Léelo. Le dará la imagen completa con todos los detalles.
Me decepcionó un poco que se explicara el problema aquí, pero nadie propuso una solución. Si desea acceder a una variable global en el alcance de la función sin que la función haga primero una var local indefinida, haga referencia a la var como
window.varName
fuente
Las variables en JavaScript siempre tienen un alcance de función amplia. Incluso si se definieron en el medio de la función, son visibles antes. Se pueden observar fenómenos similares con la función de elevación.
Dicho esto, el primero
console.log(value)
ve lavalue
variable (la interior que sombrea la exteriorvalue
), pero aún no se ha inicializado. Puede pensar en ello como si todas las declaraciones de variables se movieran implícitamente al principio de la función ( no al bloque de código más interno), mientras que las definiciones se dejan en el mismo lugar.Ver también
fuente
Hay una variable global
value
, pero cuando el control ingresa a latest
función,value
se declara otra variable, que ensombrece la global. Dado que las declaraciones de variables ( pero no las asignaciones ) en JavaScript se elevan a la parte superior del alcance en el que se declaran://value == undefined (global) var value = 10; //value == 10 (global) function test() { //value == undefined (local) var value = 20; //value == 20 (local) } //value == 10 (global)
Tenga en cuenta que lo mismo ocurre con las declaraciones de funciones, lo que significa que puede llamar a una función antes de que parezca estar definida en su código:
test(); //Call the function before it appears in the source function test() { //Do stuff }
También vale la pena señalar que cuando combina los dos en una expresión de función, la variable será
undefined
hasta que se lleve a cabo la asignación, por lo que no puede llamar a la función hasta que eso suceda:var test = function() { //Do stuff }; test(); //Have to call the function after the assignment
fuente
La forma más sencilla de mantener el acceso a las variables externas (no solo de alcance global) es, por supuesto, intentar no volver a declararlas con el mismo nombre en las funciones; simplemente no use var allí. Se aconseja el uso de reglas de nomenclatura descriptivas adecuadas . Con ellos, será difícil terminar con variables denominadas como valor (este aspecto no está necesariamente relacionado con el ejemplo de la pregunta, ya que este nombre de variable podría haberse dado por simplicidad).
Si la función puede reutilizarse en otro lugar y, por lo tanto, no hay garantía de que la variable externa realmente esté definida en ese nuevo contexto, la función Eval puede usarse. Es lento en esta operación, por lo que no se recomienda para funciones de alto rendimiento:
if (typeof variable === "undefined") { eval("var variable = 'Some value';"); }
Si la variable de alcance externo a la que desea acceder está definida en una función con nombre, entonces podría estar adjunta a la función en sí en primer lugar y luego acceder desde cualquier lugar del código, ya sea desde funciones profundamente anidadas o controladores de eventos fuera todo lo demas. Tenga en cuenta que acceder a las propiedades es mucho más lento y requeriría que cambie la forma en que programa, por lo que no se recomienda a menos que sea realmente necesario: Variables como propiedades de funciones (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");
fuente
Me estaba encontrando con el mismo problema incluso con variables globales. Descubrí que mi problema era que la variable global no persistía entre archivos html.
<script> window.myVar = 'foo'; window.myVarTwo = 'bar'; </script> <object type="text/html" data="/myDataSource.html"></object>
Intenté hacer referencia a myVar y myVarTwo en el archivo HTML cargado, pero recibí el error indefinido. En pocas palabras, descubrí que podía hacer referencia a las variables usando:
<!DOCTYPE html> <html lang="en"> <!! other stuff here !!> <script> var myHTMLVar = this.parent.myVar /* other stuff here */ </script> </html>
fuente