11521 sujets

JavaScript, DOM et API Web HTML5

Bonjour à tous,

Sur cette page (https://github.com/DrkSephy/es6-cheatsheet/blob/master/README.md) consacré à ES6, au tout début, les auteurs montre ce code :

var snack = 'Meow Mix';

function getFood(food) {
    if (food) {
        var snack = 'Friskies';
        return snack;
    }
    return snack;
}

getFood(false); // undefined


Je ne comprends pas pourquoi le undefined.

snack est déjà déclaré, je sais que les functions génère un nouveau scope mais pour autant elles peuvent accéder aux variables déclarées avant, comme ici :

var i = 5;

(function afficherNombre() {
    i = 'texte';
}());

console.log(i);


Où i prendra la valeur texte.

Le seul truc qui change entre ces deux fonctions c'est le return, quelqu'un peut il m'expliquer l'élément qu'il me manque pour comprendre ?

Merci à vous.
Modifié par MagicCarpet (03 Jun 2016 - 13:27)
Bonjour !

Je ne suis pas sure de très bien comprendre non plus... (en fait je suis à peu près certaine).

Mais sur cette page : http://www.xul.fr/ecmascript/visibilite.php

avec le dernier exemple, il est écrit :
On peut vérifier sur cet exemple que la position de la déclaration importe peu: il suffit que la variable soit déclarée quelque part dans la fonction pour qu'elle devienne locale et elle peut être déclarée après avoir été utilisée !

Mais ce n'est peut-être pas la solution... juste une pierre de plus à l'édifice 'le javascript est un langage bien étrange'...
Smiley smile
Tu peux regarder autour des fonctions anonymes ou des closures pour comprendre pourquoi la portée est différente.
var snack = 'Meow Mix';

function getFood(food) {
    if (food) {
        snack = 'Friskies';
        return snack;
    }
    return snack;
}

getFood(false); 


Confirmation.
Il faut enlever le 'var' pour trouver le résultat attendu.
Smiley smile
bzh a écrit :
Tu peux regarder autour des fonctions anonymes ou des closures pour comprendre pourquoi la portée est différente.


Pourrais-tu me donner plus de détails ? Parce que j'ai regardé justement de ce côté mais je ne vois toujours pas.

Merci.
Zelena a écrit :
var snack = 'Meow Mix';

function getFood(food) {
    if (food) {
        snack = 'Friskies';
        return snack;
    }
    return snack;
}

getFood(false); 


Confirmation.
Il faut enlever le 'var' pour trouver le résultat attendu.
Smiley smile


Oui, si tu ne met pas le var alors la variable est globale au fichier, donc accessible mais cela ne donne pas la réponse recherché Smiley smile
MagicCarpet a écrit :

Oui, si tu ne met pas le var alors la variable est globale au fichier, donc accessible mais cela ne donne pas la réponse recherché Smiley smile


Oui mais ce 'var' avait été ajouté dans une partie du programme qui n'aurait pas dû être prise en compte (le 'if (food) {...})...

Néanmoins, cela a suffit pour faire passer la variable 'snack' de globale à locale, et c'est une variable locale et non définie que le 'return' a retourné... d'où le undefined.

Enfin, c'est comme ça que je vois les choses... et apparemment avec 'let', le programme se comporte différemment...
Smiley smile
Exact, désolé pour ma réponse du vendredi pas très éclairée.

Comme le précise Zelena, ton code pourrait ressembler à ça, vu que la condition est fausse :

var snack = 'Meow Mix';

function getFood() {
    return snack;
}

getFood();
Bonjour,

En gros pour faire simple, tu définis bien une variable globale 'snack', mais vu que tu REdéfinis une AUTRE variable 'snack' localement dans ta fonction getFood() (avec l'utilisation de var), le retour n'est forcément pas bon. Mais ce qui est marrant (mais normal j'entends), c'est que ta première déclaration reste globale.
Pour t'en convaincre, teste ça :
var snack = 'Meow Mix';

function getFood(food) {
    if (food) {
        var snack = 'Friskies';
        return snack;
    }
    return snack;
}

console.info(getFood(true))
console.warn(snack)

Tu risques d'être surpris ! Smiley cligne
Conclusion, il faut virer le var dans ta fonction.
Bonjour,

En fait, lorsqu'on définit une variable avec var dans une fonction, elle est bel et bien locale à la fonction, mais la déclaration est « virtuellement » remontée au début de la fonction.
C'est ce qu'on appelle le hoisting (très bonnes explications de la MDN).


Ce qui fait que ton code est en réalité équivalent à :


function getFood(food) { 
    var snack;
    if (food) { 
        snack = 'Friskies'; // c'est la variable locale qui est modifiée
        return snack; 
    } 
    return snack;
} 


Le piège est en fait double. On a cet effet de remontée qui concerne la déclaration, mais l'initialisation, elle, ne bouge pas !

La variable est donc déclarée au début de la fonction, mais son initialisation à une valeur explicite n'a lieu que si on passe dans le if. D'où le undefined qu'on obtient en retour.

A noter de façon très intéressante que si on avait utilisé let à la place de var et si on avait supprimé le return dans le if, la fonction retournerait systématiquement la variable globale inchangée, qu'on passe dans le if ou non.
Si on omet var, comme vous l'avez constaté, c'est la variable globale, ici au contraire potentiellement modifiée au passage dans le if, qui est aussi retournée.

Moralité, vivement qu'on puisse utiliser let pour de bon, ça nous évitera bien des surprises.
Modifié par QuentinC (06 Jun 2016 - 12:06)
..... très bonne explications declaration , affectation , initialisation , this , argument, zone de portée dans (){ ici }

..... c'est la variable locale qui est modifiée OUI MAIS LOCAL A QUOI !!!! je pense que c'est les "{" "}" que ce soit après un if() ou fonction getFood()

------------------------------------
var snack = 'Meow Mix'; //window.snack
function getFood(food) {
if (food) {
var snack = 'Friskies';
return snack;
}
return window.snack;
}
getFood(false); // return 'Meow Mix'
getFood(true); // return 'Friskies'
est ce que après l execution d'un premier return dans if se comporte /remplace comme un return t de la fonction parent getFood ?
-------------------------------------
var snack = 'Meow Mix';
function getFood(food,snack) {
if (food) {
var snack = 'Friskies';
return snack;
}
return snack;
}
getFood(false,'snack'); // return 'snack' argument du getFood
getFood(true,'snack'); // return 'Friskies' celui du if
getFood n effectue pas d initialisation et d affectation pour snack dans var .
if(food) s en occupe dans laa portée "{" "}"
----------------------------------------------------------------
var snack = 'Meow Mix';
function getFood(food,snack) {
if (food) {
snack = 'Friskies';
return snack;
}
return snack;
}
getFood(false,'snack'); // return 'Friskies'
getFood(true,'snack'); // return 'Friskies'
une variable sans portée ( snack à l 'intérieur de if(){ici}) et donc sans var a une initialisation et affection effectué par la fonction parente dès son appel . Cette variable existe ( accessible) à l intérieur de sa fonction parent délimité par "{" et "}" par le code présent dans cette fonction parente.
La variable interne crée par la fonction parente prime sur le variable passé en argument de cette fonction parente !!!
----------------------------------
function afficherNombre() {
i = 'texte';
return undefined;
}
i // uncaught reference Error i is not defined()
--------------------------------
(function afficherNombre() {
i = 'texte';
return undefined;
}()); //On ne fait pas que definir une fonction , on l' execute en même temps
i //Return "texte" the string inside ""
window.i //return "texte"
console.log(i); // texte the value ?
il n y a pas de var .Donc initialisation et affectation se fait dans la fonction parente qui est le fichier c'est a à dire window .
Modifié par 75lionel (07 Jun 2016 - 01:55)
a écrit :
..... c'est la variable locale qui est modifiée OUI MAIS LOCAL A QUOI !!!! je pense que c'est les "{" "}" que ce soit après un if() ou fonction getFood()


Tu te trompes. Avec var, la portée est la fonction.
C'est avec let que la portée est le bloc entourant.

a écrit :


function getFood(food,snack) {
 if (food) {
 var snack = 'Friskies';
 return snack;
 }
 return snack;
}
getFood(false,'snack'); // return 'snack'  argument du getFood 
getFood(true,'snack'); // return 'Friskies' celui du if 


getFood n effectue pas d initialisation et d affectation pour snack dans var .
if(food) s en occupe dans laa portée "{" "}"


J'étais circonspect avec ce code, j'imaginais que var remettrait peut-être inconditionnellement à zéro la variable snack, i.e. équivalent à snack=undefined, et donc qu'à cause du hoisting et de l'affectation qui n'a pas lieu au même endroit, le retour serait undefined si on ne passe pas dans le if.

J'ai vérifié dans node et dans ce cas var n'a aucun effet, la variable utilisée est dans tous les cas le paramètre passé à la fonction.

Par contre, ne pas considérer ce fonctionnement comme étant acquis. Le mieux pour éviter un potentiel problème ou résultat inattendu, c'est d'éviter de nommer une variable avec un nom identique avec une autre variable déjà déclarée ayant une portée égale ou plus étendue.

Avec let, ça ne pose jamais aucun problème. Mais avec var c'est important d'y faire très gaffe. C'est pour ça qu'on a inventé let d'ailleurs; car var est potentiellement générateur de bugs.


a écrit :


function getFood(food,snack) {
 if (food) {
 snack = 'Friskies';
 return snack;
 }
 return snack;
}
getFood(false,'snack'); // return 'Friskies' 
getFood(true,'snack'); // return 'Friskies' 


une variable sans portée ( snack à l 'intérieur de if(){ici}) et donc sans var a une initialisation et affection effectué par la fonction parente dès son appel . Cette variable existe ( accessible) à l intérieur de sa fonction parent délimité par "{" et "}" par le code présent dans cette fonction parente.
La variable interne crée par la fonction parente prime sur le variable passé en argument de cette fonction parente !!!


Là par contre c'est faux, c'est bien la variable passée en paramètre de la fonction qui est modifiée dans le if et aucune variable locale au bloc if n'est crée.
C'est ce qui se serait passé si tu avais utilisé let. Souviens-toi, les variables déclarées avec var sont toujours locales à la totalité de la fonction et non pas au bloc comme let; c'est là une des différences fondamentales.
Modifié par QuentinC (07 Jun 2016 - 06:15)
mea culpa ..if(){} n est pas une fonction ......toujours se remettre en question .... Smiley biggrin

les variables déclarées avec var sont toujours locales à la totalité de la fonction.
Quelle est alors la "fonction" dans le cas ou var est dans un fichier en dehors de toutes fonctions?

Entre une variable de même nom passée en argument d'une fonction et une variable utilisée ( apparait avec var et sans var ) dans la fonction ; quelle est la portée de ces 3 variables ?

Sinon à lire sur scope/this en javascript entre nodejs et navigateur et passage des variables par valeur/référence .

Pour le dernier lien( valeur référence) ; essayer de décrire ce qui se passe en utilisant les termes communs ne fonctionne pas pour décrire ce qui se passe car la réalité ( ce qui se passe .. le contexte ) est différente ...Car la nature de la variable passée en argument conditionne le passage par valeur ou par référence ..... ( est ce clair ? )

plus j essaie de comprendre et moins je comprends .....( "fonction != expression != litteral ")
Modifié par 75lionel (07 Jun 2016 - 09:47)
Lionel, c'est parfois difficile de te suivre.

a écrit :
Quelle est alors la "fonction" dans le cas ou var est dans un fichier en dehors de toutes fonctions?


C'est là que Node et les navigateurs ne font pas la même chose.

Dans node, chaque fichier est à considérer comme étant lui-même une fonction; ou en d'autres termes chaque fichier JS exécuté dans node est « virtuellement encadré » par
(function(){ /* ton code */ })();

Résultat, une variable déclarée avec var dans un script Node est locale au module.
Si ça peut t'aider à prendre conscience de cette séparation entre d'abord l'encapsulation puis ensuite l'exécution immédiate, Si tu regardes la documentation du langage lua, qui fait un peu la même chose que Node à ce sujet, tu verras qu'il existe une fonction load, qui permet de charger un fichier .lua et retourne une fonction mais ne l'exécute pas. D'ailleurs, en lua, on a le droit d'écrire return en-dehors de toute fonction.

Alors que dans un navigateur, il n'y a pas cet étape d'encadrement; la variable est globale.




Pour ton deuxième exemple avec this, en fait on utilise une des bêtise fondamentales de JavaScript, qui veut que this représente l'objet global s'il ne vaut pas autre chose.
IL faut prendre l'habitude de systématiquement développer en mode strict où this vaut null si la fonction n'a pas été bindée ou n'est pas clairement une méthode. ca évite des surprises de ce genre.

a écrit :
Pour le dernier lien( valeur référence) ; essayer de décrire ce qui se passe en utilisant les termes communs ne fonctionne pas pour décrire ce qui se passe car la réalité ( ce qui se passe .. le contexte ) est différente ...Car la nature de la variable passée en argument conditionne le passage par valeur ou par référence ..... ( est ce clair ? )


Je ne suis absolument pas d'accord avec les réponses données sur stack.

En JavaScript comme dans la quasi-totalité des autres langages modernes à l'exception du C++ (p.ex. Java, python, PHP, ...), tout se passe toujours par référence; ou plus exactement, on devrait dire, tout est toujours passé par pointeur.
ON a juste faussement l'impression qu'il y a parfois des passages par valeur car certains objets sont non modifiables (immuables)
En C++ c'est différent, car on a le choix et la logique est inversée, tout est passé par valeur si on ne précise rien.

Pour reprendre l'exemple de stack, quand dans la fonction on écrit x++, ça équivaut à x=x+1. Il y a affectation d'une nouvelle référence à la variable x.
Dès lors la références initiale reste intacte ! et tu as faussement eu l'impression que, parce que c'est un nombre, le passage s'est effectué par valeur.

Mais, en fait, c'est faux

Comparons donc ce qui est comparable :


var obj = { a: 1, b: 2};
function f (o) {
o = { c: 3, d : 4 };
}
console.log(obj); // {a: 1, b: 2 }. 


Ben oui, voyons. ici ça ne viendrait à l'idée de personne que la sortie puisse être {c: 3, d: 4}.
Note bien la ressemblance pourtant. remplace par o++, ou o=o+1, essaie de passer une string ou un nombre, ou autre chose encore. Tu vois bien que le processus n'est en rien différent que tu passes un objet ou un type primitif.

Maintneant on va s'amuser autrement :


var obj = { a: 1, b: 2 };
function f (o) {
o.c = 3;
}
console.log(obj.c); // undefined
f(obj);
console.log(obj.c); // 3


Jusque là rien d'extraordinaire.


var num = 3.14;
console.log(num.c); // undefined
f(num);
console.log(num.c); // undefined. 


Ah ben zut alors, je te mens depuis 5 minutes ?
Non. C'est juste qu'en JavaScript ça ne fonctionne pas, parce que les nombres sont des types primitifs et non de vrais objets. Mais dans certains autres langages moins mainstream, on peut le faire...
En guise de bonne blague, remplace 3.14 par new Number(3.14). ET paf, ça marche maintenant.


En fait je ne peux pas te prouver que j'ai raison sur un passage systématique par référence. Car si on a défini que les strings étaient des objets imuables par exemple, c'est justement pour éviter des choses qui ne nous paraissent pas naturelles.
Mais informatiquement parlant, un passage par valeur signifie dupliquer la donnée en mémoire. Pour les nombres on s'en fiche, c'est 4 ou 8 octets; mais si on a une string qui contient le data URI en base64 d'une vidéo de plusieurs Mo, heureusement qu'elle n'est pas intégralement recopiée à chaque passage ou sortie de fonction.

Voilà...
Merci pour votre réponse ..... je ne "connaissais pas" les concepts d'espace mémoire différent=mutable , identique=immutability
[-ajout-]
pour ne pas polluer ce post je pose ma question sur celui ci
Modifié par 75lionel (10 Jun 2016 - 00:17)
surcepost a écrit :

QuentinC a écrit :
Lionel, c'est parfois difficile de te suivre.
SolidSnake a écrit :
PARFOIS ??? Smiley eek
Smiley ravi


javascript est très facile à lire mais OUAHHH difficile a suivre !!!!
Modifié par 75lionel (10 Jun 2016 - 00:02)