Comment sont gérées les déclarations de fonction?
var abc = '';
if(1 === 0){
function a(){
abc = 7;
}
}else if('a' === 'a'){
function a(){
abc = 19;
}
}else if('foo' === 'bar'){
function a(){
abc = 'foo';
}
}
a();
document.write(abc); //writes "foo" even though 'foo' !== 'bar'
Cet exemple produit différentes sorties dans Chrome et Firefox. Chrome génère foo
tandis que FF génère 19
).
Lorsque cette question a été posée, ECMAScript 5 (ES5) était répandu. En mode strict d'ES5, les déclarations de fonctions ne peuvent pas être imbriquées à l'intérieur d'un bloc if
comme indiqué dans la question. En mode non strict, les résultats étaient imprévisibles. Différents navigateurs et moteurs ont implémenté leurs propres règles pour gérer les déclarations de fonctions à l'intérieur des blocs.
Depuis 2018, de nombreux navigateurs prennent en charge ECMAScript 2015 (ES2015) dans la mesure où les déclarations de fonctions sont désormais autorisées dans les blocs . Dans un environnement ES2015, une déclaration de fonction à l'intérieur d'un bloc sera portée à l'intérieur de ce bloc. Le code de la question entraînera une erreur de fonction indéfinie car la fonction a
est uniquement déclarée dans la portée des instructions if
et n'existe donc pas dans la portée globale.
Si vous devez définir une fonction de manière conditionnelle, vous devez utiliser expressions de fonction .
De http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/
En javascript, vous avez la déclaration de fonction:
function foo() {
}
et expression de fonction
var foo = function() {
}
Citant de http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting
"Les déclarations de fonction et les variables de fonction sont toujours déplacées (" hissées ") en haut de leur portée JavaScript par l'interpréteur JavaScript".
Donc, ce qui s'est passé dans votre premier exemple, c'est que la déclaration de fonction de function a()
, est hissée en haut de la portée Javascript, produisant ainsi 'foo' même si le if est évalué à false
Considérez var foo
Comme une instruction Javascript normale, elle n'est exécutée que pendant l'exécution de votre javascript, contrairement à function foo()
, c'est pourquoi ce qui suit est valide:
alert(foo());
function foo() {
return 'gw ganteng';
}
Ici, function foo()
est analysé par l'analyseur, mettant foo()
dans la portée actuelle, avant d'essayer d'appeler alert(foo())
http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/
Dans l'exécution JavaScript, il y a Context (que ECMA 5 décompose en LexicalEnvironment, VariableEnvironment et ThisBinding) et Process (un ensemble d'instructions à invoquer en séquence). Les déclarations contribuent à VariableEnvironment lorsque la portée d'exécution est entrée. Ils sont distincts des déclarations (comme le retour) et ne sont pas soumis à leurs règles de procédure.
L'ECMA-262 v5 nécessite des implémentations pour enregistrer toutes les déclarations de fonctions et de variables lors de la première passe lors de la saisie de tout nouveau contexte d'exécution global ou de niveau fonction. Chrome le fait techniquement ici car il regarde à l'intérieur des blocs else
et then
et enregistre a()
avant l'exécution. Malheureusement il produit les résultats les plus illisibles.
FF attend jusqu'à ce qu'il évalue l'instruction if avant d'évaluer et d'ajouter des déclarations de fonction et de variable au contexte actuel. BTW. Les deux navigateurs le font de cette façon dans les clauses catch et finally.
Il s'agit vraiment de deux implémentations ECMA différentes traitant d'une fonctionnalité qui ne devrait pas être là pour commencer. Le scénario présenté montre pourquoi les déclarations de fonction ne doivent pas être à l'intérieur des instructions de flux de contrôle.