Ce phénomène est connu sous le nom de: Levage variable JavaScript .
A aucun moment vous n'accédez à la variable globale de votre fonction; vous n'accédez jamais qu'à la value
variable locale .
Votre code équivaut à ce qui suit:
var value = 10;
function test() {
var value;
console.log(value);
value = 20;
console.log(value);
}
test();
Vous êtes toujours surpris undefined
?
Explication:
C'est quelque chose que chaque programmeur JavaScript rencontre tôt ou tard. En termes simples, les variables que vous déclarez sont toujours hissées au sommet de votre fermeture locale. Ainsi, même si vous avez déclaré votre variable après le premier console.log
appel, elle est toujours considérée comme si vous l'aviez déclarée avant cela.
Cependant, seule la partie déclaration est soulevée; la cession, en revanche, ne l'est pas.
Ainsi, lorsque vous avez appelé pour la première fois console.log(value)
, vous faisiez référence à votre variable déclarée localement, à laquelle rien ne lui est encore assigné; par conséquent undefined
.
Voici un autre exemple :
var test = 'start';
function end() {
test = 'end';
var test = 'local';
}
end();
alert(test);
Que pensez-vous que cela alertera? Non, ne vous contentez pas de lire, pensez-y. Quelle est la valeur de test
?
Si vous avez dit autre chose que start
, vous vous êtes trompé. Le code ci-dessus est équivalent à ceci:
var test = 'start';
function end() {
var test;
test = 'end';
test = 'local';
}
end();
alert(test);
afin que la variable globale ne soit jamais affectée.
Comme vous pouvez le voir, peu importe où vous placez votre déclaration de variable, elle est toujours hissée en haut de votre fermeture locale.
Note latérale:
Cela s'applique également aux fonctions.
Considérez ce morceau de code :
test("Won't work!");
test = function(text) { alert(text); }
ce qui vous donnera une erreur de référence:
Uncaught ReferenceError: le test n'est pas défini
Cela dérange beaucoup de développeurs, car ce morceau de code fonctionne bien:
test("Works!");
function test(text) { alert(text); }
La raison à cela, comme indiqué, est que la partie d'affectation n'est pas hissée. Ainsi, dans le premier exemple, lors de l' test("Won't work!")
exécution, la test
variable a déjà été déclarée, mais la fonction ne lui est pas encore affectée.
Dans le deuxième exemple, nous n'utilisons pas l'affectation de variables. Nous utilisons plutôt une syntaxe de déclaration de fonction appropriée, ce qui permet de hisser complètement la fonction.
Ben Cherry a écrit un excellent article à ce sujet, intitulé à juste titre JavaScript Scoping and Hoisting .
Lis le. Cela vous donnera une image complète en détail.
J'ai été quelque peu déçu que le problème ici soit expliqué, mais personne n'a proposé de solution. Si vous souhaitez accéder à une variable globale dans la portée de la fonction sans que la fonction ne crée d'abord une variable locale non définie, référencez la variable comme
window.varName
la source
Les variables en JavaScript ont toujours une portée à l'échelle de la fonction. Même s'ils ont été définis au milieu de la fonction, ils sont visibles avant. Des phénomènes similaires peuvent être observés avec le levage de fonction.
Cela étant dit, le premier
console.log(value)
voit lavalue
variable ( la variable intérieure qui fait de l'ombre à l'extérieurvalue
), mais elle n'a pas encore été initialisée. Vous pouvez y penser comme si toutes les déclarations de variables étaient implicitement déplacées au début de la fonction ( pas le bloc de code le plus interne), tandis que les définitions sont laissées au même endroit.Voir également
la source
Il existe une variable globale
value
, mais lorsque le contrôle entre dans latest
fonction, une autrevalue
variable est déclarée, qui occulte la variable globale. Étant donné que les déclarations de variables ( mais pas les affectations ) en JavaScript sont hissées au sommet de la portée dans laquelle elles sont déclarées://value == undefined (global) var value = 10; //value == 10 (global) function test() { //value == undefined (local) var value = 20; //value == 20 (local) } //value == 10 (global)
Notez qu'il en va de même pour les déclarations de fonction, ce qui signifie que vous pouvez appeler une fonction avant qu'elle ne semble être définie dans votre code:
test(); //Call the function before it appears in the source function test() { //Do stuff }
Il est également intéressant de noter que lorsque vous combinez les deux dans une expression de fonction, la variable sera
undefined
jusqu'à ce que l'affectation ait lieu, vous ne pouvez donc pas appeler la fonction tant que cela ne se produit pas:var test = function() { //Do stuff }; test(); //Have to call the function after the assignment
la source
Le moyen le plus simple de conserver l'accès aux variables externes (pas seulement de portée globale) est, bien sûr, d'essayer de ne pas les re-déclarer sous le même nom dans les fonctions; ne pas utiliser var ici. L'utilisation de règles de dénomination descriptives appropriées est conseillée. Avec ceux-ci, il sera difficile de se retrouver avec des variables nommées comme valeur (cet aspect n'est pas forcément lié à l'exemple de la question car ce nom de variable aurait pu être donné par simplicité).
Si la fonction peut être réutilisée ailleurs et qu'il n'y a donc aucune garantie que la variable externe réellement définie dans ce nouveau contexte, la fonction Eval peut être utilisée. Il est lent dans cette opération, il n'est donc pas recommandé pour les fonctions exigeantes en performances:
if (typeof variable === "undefined") { eval("var variable = 'Some value';"); }
Si la variable de portée externe à laquelle vous souhaitez accéder est définie dans une fonction nommée, elle peut être attachée à la fonction elle-même en premier lieu, puis accessible de n'importe où dans le code - que ce soit à partir de fonctions profondément imbriquées ou de gestionnaires d'événements en dehors de tout le reste. Notez que l'accès aux propriétés est beaucoup plus lent et vous obligerait à changer la façon dont vous programmez, donc ce n'est pas recommandé sauf si c'est vraiment nécessaire: Variables en tant que propriétés de fonctions (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");
la source
Je rencontrais le même problème même avec des variables globales. Mon problème, j'ai découvert, était que la variable globale ne persistait pas entre les fichiers html.
<script> window.myVar = 'foo'; window.myVarTwo = 'bar'; </script> <object type="text/html" data="/myDataSource.html"></object>
J'ai essayé de référencer myVar et myVarTwo dans le fichier HTML chargé, mais j'ai reçu l'erreur non définie. Longue histoire / jour court, j'ai découvert que je pouvais référencer les variables en utilisant:
<!DOCTYPE html> <html lang="en"> <!! other stuff here !!> <script> var myHTMLVar = this.parent.myVar /* other stuff here */ </script> </html>
la source