2010-04-10 8 views
1

j'ai remarqué une étrange (et à ma connaissance un comportement non défini, par l'ECMA 3.0 Spec au moins), prenez l'extrait suivant:Comportement des variables locales JavaScripts avec() - déclaration

var foo = { bar: "1", baz: "2" }; 
alert(bar); 
with(foo) { 
    alert(bar); 
    alert(bar); 
} 
alert(bar); 

Il plante en Firefox et Chrome, car "barre" n'existe pas dans la première alerte(); déclaration, c'est comme prévu. Mais si vous ajoutez une déclaration de bar à l'intérieur de la avec() - déclaration, il ressemble à ceci:

var foo = { bar: "1", baz: "2" }; 
alert(bar); 
with(foo) { 
    alert(bar); 
    var bar = "g2"; 
    alert(bar); 
} 
alert(bar); 

Il produira les résultats suivants:

undefined, 1, g2, undefined

Il semble que si vous créez une variable à l'intérieur d'une déclaration with() - la plupart des navigateurs (testés sur Chrome ou Firefox) feront en sorte que cette variable existe aussi en dehors de cette portée, elle est simplement définie sur undefined. Maintenant, de mon bar en perspective devrait exister que dans le avec() - déclaration, et si vous faites l'exemple encore plus étrange:

var foo = { bar: "1", baz: "2" }; 
var zoo; 
alert(bar); 
with(foo) { 
    alert(bar); 
    var bar = "g2"; 
    zoo = function() { 
     return bar; 
    } 
    alert(bar); 
} 
alert(bar); 
alert(zoo()); 

Il produira ceci:

undefined, 1, g2, undefined, g2

Ainsi, le bar intérieur l'instruction with() n'existe pas en dehors de celle-ci, mais l'exécution crée automatiquement une barre nommée variable qui n'est pas définie dans sa portée de niveau supérieur (global ou fonction) mais cette variable ne fait pas référence à la même qu'à l'intérieur l'instruction with() -, et cette variable n'existera que si une instruction with() - a barre nommée fiable qui est défini à l'intérieur.

Très étrange, et incohérent. Quelqu'un at-il une explication à ce comportement? Il n'y a rien dans l'ECMA Spec à ce sujet.

Répondre

1

Cela résulte de ECMA-262, 3e édition, § 12.2, comme

Les variables sont créées lorsque le champ d'exécution est saisie. Un Le bloc ne définit pas une nouvelle étendue d'exécution.

et

Une variable avec une Initialiser reçoit la valeur de son AssignmentExpression lorsque le VariableStatement est exécutée, et non lorsque la variable est créée.

Cela signifie que vous devez penser à une déclaration initialiseur en deux étapes séparées: d'abord, vous créez une nouvelle variable dans le périmètre de la fonction locale (globale) lors de l'entrée de la fonction (exécution du script), mais vous N'attribuez pas de valeur avant d'avoir atteint l'instruction. Dans vos exemples, à ce stade, la chaîne de portée a changé en raison de with(), et l'attribution voit la surcharge varriable, laissant le nouvellement créé undefined.

Cela signifie

with({ foo : 42 }) { 
    var foo = 'bar'; 
} 

est analysé comme

with({ foo : 42 }) { 
    var foo; 
    foo = 'bar'; 
} 

qui est équivalent à

var foo; 
({ foo : 42 }).foo = 'bar'; 

et donc laissant foo non initialisée.

+0

Bonne explication. Et une autre bonne raison de ne pas utiliser 'avec '! – bobince

0

Je pense que vous vous méprenez un peu sur la portée. Pas que je vous blâme, si vous venez de plus de autres langues, il vous fait aller "WTF?". Javascript a ce que j'appelle "FUBAR Scoping" (je suis sûr qu'il y a un terme approprié ici mais je n'ai jamais pris la peine de l'apprendre ... sachant comment ça fonctionne> sachant comment ça s'appelle, au moins pour moi).

Let's make the example even simpler:

with({}) { 
    var foo = "test";​ 
} 
alert(foo); //alerts "test" 

with() permet de définir au niveau des blocs d'une variable, pour être passé dans un champ (good answer demonstrating this here). Mais ... il ne limite pas la variable à ce champ, il est disponible par la suite, here's a better example:

with({ bar: 1}) { 
    this.foo = "test"; 
    alert(bar); //alerts: 1"1" 
    alert(this); //alerts: "DOMwindow" 
} 
alert(foo); 

Si with() était une fermeture à l'élément que vous avez affaire, alors this se rapporterait à la { bar: 1} objet que nous avons passé dans le avec, mais ce n'est pas le cas :). this dans ce cas est toujours le contexte global de window. Puisque vous exécutez toujours dans ce contexte, toutes les variables que vous définissez ici seront disponibles dans ce contexte, et donc disponibles plus tard dans la fonction, après le with(), car elles sont définies beaucoup plus haut que vous ne le pensez. Intuition vous indique qu'il est défini dans une portée plus limitée: dans le with() ... mais le javascript est différent, tout dépend du contexte dans lequel votre fermeture s'exécute.

J'ai essayé de trouver une bonne description de ce comportement qui donne une beaucoup plus d'exemples de portée, this is the best I could find for you. Globalement, il s'agit plus d'une question de fermeture/scoping que with() explicitement, ce site couvre le concept global plus en détail au cas où j'ai fait un travail terrible de l'expliquer ... Je sais que c'est un concept en arrière à bien des égards, désolé!

Questions connexes