Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

débutant variable globale et 'var'

7 réponses
Avatar
KooK
Bonjour,

J'ai 2 boutons. L'un crée un objet que l'autre utilise, à chaque fois
sur des actions 'onclick'.

Jusque là, j'utilisais la commande 'var biere = new Burps(1)' pour la
création. Du coup mon objet était inconnu de l'autre bouton. Si je
supprime le mot clef 'var', ça marche.

Ma question est : faut-il déclarer la variable avec 'var biere' dans
l'en-tête du fichier ou est-ce totalement inutile ? A savoir que je veux
que mon code soit le plus propre possible, pour être compris par d'autre.

Merci,
KooK


<code>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
<head>
<script type= "text/javascript">
/*<![CDATA[*/
function Burps(id)
{
this.id = id;

alert("l'objet Burps (" + id + ") est cree.");
this.rote = function()
{
alert(id + " : <<BURPS>> !");
}
}

var biere;
/*]]>*/
</script>
</head>
<body>
<input type='button' id='un' value='un' onclick='javascript: biere =
new Burps(1);'>
<input type='button' id='deux' value='deux' onclick='javascript:
biere.rote();'>
</body>
</html>
</code>

7 réponses

Avatar
Elegie
KooK wrote:

Hello,

J'ai 2 boutons. L'un crée un objet que l'autre utilise, à chaque fois
sur des actions 'onclick'.

Jusque là, j'utilisais la commande 'var biere = new Burps(1)' pour la
création. Du coup mon objet était inconnu de l'autre bouton. Si je
supprime le mot clef 'var', ça marche.


Version courte: le mot-clé "var", utilisé dans une fonction, rend la
variable locale à la fonction; l'absence du mot clé dans la création
d'une variable à l'intérieur d'une fonction rend cette variable globale.

Version longue: ce qui suit explique en détail le fonctionnement de la
création et de la résolution de variables en javascript. Un éclairage
particulier est apporté sur les notions de "contexte d'éxecution",
"scope chain", "variable object", essentielles à la compréhension de ce
fonctionnement.

Il existe plusieurs types de codes executables en javascript:
_global_code_ (exécuté lorsque le script est chargé), _function_code_
(exécuté lorsqu'une fonction est appelée) et _eval_code_ (lorsque la
fonction native eval est appelée). L'exécution de chacun de ces codes a
lieu dans ce qu'on appelle un _execution_context_. Dans un script, il y
a donc a minima un contexte d'exécution (le contexte global) et
généralement plusieurs (un par fonction, un par appel de la fonction eval).

A chaque contexte d'exécution est associé un _Variable_Object_ (objet
Variable). Cet objet peut être envisagé comme un objet javascript
normal, sauf qu'il est interne, et non visible par le programmeur. Son
rôle sera de référencer les différentes variables associées au contexte
d'exécution. Le processus appelé _Variable_Instantiation_ entre en jeu
dès la création de cet objet, et avant toute exécution du texte source:
le bloc de texte exécutable est scanné, et pour chaque déclaration faite
avec le mot clé "var", pour chaque déclaration de fonction (suivant la
syntaxe _FunctionDeclaration_, par opposition à celle appelée
_FunctionExpression_), pour chaque argument de fonction (si nous sommes
dans une fonction), une propriété est créée sur l'objet Variable associé
au contexte d'exécution, le nom de cette propriété est égal au nom de la
variable/fonction/argument. Pour le moment, la valeur même de cette
propriété est indéfinie, elle ne sera renseignée que lors de l'exécution
réelle du texte source.

Un autre objet, appelé _scope_chain_, est également associé au contexte
d'exécution. Cet objet servira dans la résolution des identifiants du
texte source. Il contient a minima l'objet variable du contexte
d'exécution courant, et souvent plusieurs autres (ceux des scope chains
des _contextes_appelants_, ceux créés par les instructions _with_ ou
_catch_).

Récapitulons: un contexte d'exécution a été créé, lui ont été associés
un objet Variable, aux propriétés alimentées les déclarations de
variables, de fonctions ou d'arguments de fonction (si nous sommes dans
une fonction), ainsi qu'un objet scope chain. Maintenant, l'exécution du
texte source peut commencer.

Cette exécution va probablement faire appel, plusieurs fois, au
processus dit de _résolution_d'identifiant_. Par exemple, "fooB"
implique de trouver la propriété foo dans l'un des objets variables de
la scope chain, et de lui assigner la valeur 42.

Si la variable "foo" a été déclarée avec le mot-clé var, dans le
contexte d'exécution courant ou dans un contexte appelant, ou encore si
cette variable "foo" est le nom d'un argument de fonction (si nous
sommes dans une fonction), alors la propriété sera bien trouvée dans la
scope chain, puis mise à jour. C'est pour cela que, dans le cadre d'une
variable définie dans une fonction à l'aide du mot-clé "var" (et donc
enregistrée sur l'objet Variable de cette fonction), on peut la
considérer comme "locale" à cette fonction (car disponible dans les
scope chains possédant l'objet Variable de la fonction courante, soit
uniquement la fonction elle-même et ses éventuelles fonctions internes).

En revanche, si cette variable "foo" n'a jamais été déclarée, elle ne
sera pas trouvée dans la scope chain. ECMAscript précise alors qu'une
propriété "foo" sera créée sur le _global_object_ (qui est l'objet
Variable associé au contexte d'exécution global), et assignera la bonne
valeur à cette propriété (ECMAScript 10.1.4, objet non trouvé, Reference
null renvoyée, puis ECMAScript 8.7.2, appel de PutValue sur une
Reference de type null). Ce qui revient à dire qu'une variable non
déclarée à l'aide du mot-clé "var" sera en fait déclarée, lors de
l'exécution du texte source, à la volée, en douce, sur l'objet global.

Ma question est : faut-il déclarer la variable avec 'var biere' dans
l'en-tête du fichier ou est-ce totalement inutile ? A savoir que je veux
que mon code soit le plus propre possible, pour être compris par d'autre.


En termes purement technique, ce n'est effectivement pas nécessaire.
Toutefois, les bonnes pratiques javascript recommandent effectivement
l'emploi systématique du mot-clé "var", en espace local ou global, pour
de simples raisons de lisibilité (afin que le programmeur évite de se
demander si le mot-clé "var" n'a pas été oublié, et ne se perde pas dans
ses variables locales, semi-locales et globales).

Par ailleurs, on essaie généralement de limiter le nombre de variables
globales, en définissant une variable globale unique qui jouera le rôle
d'espace de noms.

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">


IE jusquà sa version 6 ne supporte pas le XHTML, et j'ignore si la
version 7 a fait des progrès dans ce domaine. A moins que vous ne
souhaitiez vraiment utiliser une application XML, utilisez plutôt HTML4.

<input type='button' id='un' value='un' onclick='javascript: biere =


L'utilisation de "javascript:" ne sert que si un autre langage de script
(par exemple VBScript, ou PerlScript) est utilisé dans la page; sinon ça
ne devient qu'un label javascript inutile, car duquel on ne sort pas à
l'aide d'instructions "break" ou "continue".


HTH,
Elegie.

Avatar
Pierre Goiffon
Elegie wrote:
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">


IE jusquà sa version 6 ne supporte pas le XHTML, et j'ignore si la
version 7 a fait des progrès dans ce domaine. A moins que vous ne
souhaitiez vraiment utiliser une application XML, utilisez plutôt HTML4.


Pour un peu plus de détails voir :
http://pgoiffon.free.fr/info/inet/html_ou_xhtml.php


Avatar
Olivier Miakinen

Version longue: [...]


Merci pour ces explications. J'en ai vu d'autres qui semblaient très
intéressantes sur <http://www.jibbering.com/faq/faq_notes/closures.html>
mais malheureusement c'est en anglais.

[...]

A chaque contexte d'exécution est associé un _Variable_Object_ (objet
Variable). [...] Le processus appelé _Variable_Instantiation_ entre en jeu
dès la création de cet objet, et avant toute exécution du texte source:
le bloc de texte exécutable est scanné, et pour chaque déclaration faite
avec le mot clé "var", [...] une propriété est créée sur l'objet Variable
associé au contexte d'exécution, [...]

[...]

Si la variable "foo" a été déclarée avec le mot-clé var, dans le
contexte d'exécution courant ou dans un contexte appelant, ou encore si
cette variable "foo" est le nom d'un argument de fonction (si nous
sommes dans une fonction), alors la propriété sera bien trouvée dans la
scope chain, puis mise à jour. [...]

En revanche, si cette variable "foo" n'a jamais été déclarée, elle ne
sera pas trouvée dans la scope chain. ECMAscript précise alors qu'une
propriété "foo" sera créée sur le _global_object_ [...]


Pour voir si j'ai bien compris... dans le cas suivant, la variable foo
sera créée sur l'objet global :

function bar()
{
foo = 5;
}

Mais dans chacun des trois cas suivants elle sera locale :

function bar()
{
var foo = 5;
}

function bar()
{
foo = 5;
... plein de code ...
var foo;
}

function bar()
{
foo = 5;
... plein de code ...
if (2 == 3) { var foo; }
}

C'est bien ça, même dans le dernier cas où le test ne réussit jamais ?

Avatar
Elegie
Olivier Miakinen wrote:

Hello,

Merci pour ces explications. J'en ai vu d'autres qui semblaient très
intéressantes sur <http://www.jibbering.com/faq/faq_notes/closures.html>
mais malheureusement c'est en anglais.


Cet article reste, à mon avis, l'un des plus brillants écrits sur
javascript.

<mode="grandpa">

A l'époque de sa publication, "en ce temps-là", les closures n'étaient
quasiment pas employées, en dehors de quelques auteurs dotés d'un
background en programmation fonctionnelle, qui les utilisaient pour
résoudre élégamment des problèmes de scoping. Douglas Crockford avait
également commencé à en saisir l'intérêt en exposant quelques idées sur
l'implémentation de membres privés et publics en javascript, mais sans
toutefois approfondir jusqu'à l'analyse du paradigme javascript.

C'est véritablement Richard Cornford qui, à travers cet article (et un
autre traitant des membres privés statiques), ainsi que ses
interventions sur comp.lang.javascript, parvint à faire une synthèse
remarquable du sujet, articulant dans une réflexion très documentée les
côtés objets (prototypage) et fonctionnels (objets de première classe,
closures) du langage, définissant quelques patterns propres javascript,
aujourd'hui reconnus et utilisés par la plupart des professionnels.

<URL:http://www.litotes.demon.co.uk/js_info/private_static.html>

</mode>

Hélas, une traduction française complète n'a jamais, à ma connaissance,
été proposée; la complexité du sujet, le style très technique de
l'auteur, contribuent à rendre la tâche ardue.

<snip>

Mais dans chacun des trois cas suivants elle sera locale :

function bar()
{
var foo = 5;
}

function bar()
{
foo = 5;
... plein de code ...
var foo;
}

function bar()
{
foo = 5;
... plein de code ...
if (2 == 3) { var foo; }
}

C'est bien ça, même dans le dernier cas où le test ne réussit jamais ?


Oui, tout à fait. Le processus d'instantiation de variables a lieu avant
l'exécution du texte source, et recherche la totalité des instructions
"var", sans tenter d'interpréter le code.

Un autre exemple intéressant (commentez le "var foo" pour tester) :

---
function bar() {
foo = 5;
does_not_exist();
var foo;
}
try { bar() } catch(e) {} finally {alert(typeof foo);}
---


Cheers,
Elegie.

Avatar
Olivier Miakinen

Merci pour ces explications. J'en ai vu d'autres qui semblaient très
intéressantes sur <http://www.jibbering.com/faq/faq_notes/closures.html>
mais malheureusement c'est en anglais.


Cet article reste, à mon avis, l'un des plus brillants écrits sur
javascript.

[...]
<URL:http://www.litotes.demon.co.uk/js_info/private_static.html>


C'est imprimé, je vais essayer de le lire. Merci !

function bar()
{
foo = 5;
... plein de code ...
if (2 == 3) { var foo; }
}

[ la variable est locale ] même dans le dernier cas où le test ne
réussit jamais ?


Oui, tout à fait. Le processus d'instantiation de variables a lieu avant
l'exécution du texte source, et recherche la totalité des instructions
"var", sans tenter d'interpréter le code.


Amusant. Je n'ai bien sûr aucune envie d'écrire un tel code, mais il est
intéressant de comprendre les cas extrêmes pour ne pas être surpris dans
les cas plus simples.

Par exemple, le code suivant est probablement valide, même si la portée
de la variable idx n'est pas celle qu'a supposé le programmeur :

function bar()
{
...
if (un_test) {
var idx;
for (idx = 0; idx <= 5; idx++) {
...
}
} else {
var idx;
for (idx = 5; idx >= 0; idx--) {
...
}
}
}

Un autre exemple intéressant (commentez le "var foo" pour tester) :

---
function bar() {
foo = 5;
does_not_exist();
var foo;
}
try { bar() } catch(e) {} finally {alert(typeof foo);}
---


J'essaierai de retour chez moi.


Avatar
Elegie
Olivier Miakinen wrote:

Amusant. Je n'ai bien sûr aucune envie d'écrire un tel code, mais il est
intéressant de comprendre les cas extrêmes pour ne pas être surpris dans
les cas plus simples.


Entièrement d'accord.

Par exemple, le code suivant est probablement valide, même si la portée
de la variable idx n'est pas celle qu'a supposé le programmeur :


Le code est en effet valide, ECMAScript, au cours du processus
d'instantiation de variables, va réaliser que la propriété nommée existe
déjà sur l'objet variable, et va poursuivre sa route sans toucher aux
attributs et à la valeur de cette propriété.

L'exemple que vous proposez est intéressant à plus d'un titre; non
seulement il met en relief l'absence de block-scoping en javascript (qui
surprendra effectivement quelques programmeurs), mais il pose aussi la
question des circonstances dans lesquelles un scope est créé et/ou altéré.

L'un des exemples les plus intéressants, à mon avis, est celui qui
utilise l'instruction "with" avec un objet anonyme. Le scope associé au
contexte d'exécution est altéré, l'objet défini par l'instruction "with"
étant inséré avant l'objet Variable du contexte d'exécution (ou avant
d'autres objets insérés en cours de route, par exemple avec d'autres
instructions "with" ou "catch"). Le résultat est un espace privé
statique, que l'on peut même enrichir d'une méthode "define", pour
déclarer lors du runtime de nouvelles propriétés, si besoin.

---
with ({
// espace privé statique
foo1 : null,
foo2 : null,
foo3 : null,

define : function (p, v) {
this[p]=v;
},

exists : function (p) {
return typeof this[p] != "undefined" ;
}

}){

function Foo(){
// constructeur
}

}
---

Avatar
KooK
Merci, je ne m'attendais pas à une réponse aussi complète.

Encore merci.
KooK