Dans ce second billet nous allons aborder un objet clé pour la compréhension du Javascript : les fonctions. 

Beaucoup d »entre vous ont déjà utilisé les fonctions en Javascript et savent qu’il s’agit d’objets possédant des propriétés et des méthodes au même titre que Array, Date, … Nous commencerons par un rappel basique sur les fonctions en js, puis nous aborderons les fonctions internes et les fonctions anonymes auto-exécutantes.

Voici 2 façons de déclarer une fonction : 

function addition(a, b) {
    return a + b;
}

alert(addition(5,5));

var maFonction = function multiplication(a, b) {
    return a * b;
}

alert(maFonction(5,5));

La seule particularité notable est que l »on peut affecter une fonction à une variable. Cette variable contient une référence qui pointe sur la fonction. Voici un petit exemple illustrant ce fonctionnement :

var bar = function fonction1() { alert("A"); } //bar est un pointeur sur la fonction1
var foo = bar;  //le poitneur est copié, maintenant foo pointe également sur la fonction1
bar = function fonction2() { alert("B"); };  //bar pointe maintenant sur la fonction2
foo();  //foo pointe quand à lui toujours sur la fonction1

Le terme de « pointeur » est utilisé par abus de langage car, en réalité, cela ne correspond pas aux pointeurs que vous avez pu rencontrer en C, C++, C#,.. Mais nous reviendrons sur ce sujet plus tard 

Bon tout cela reste très basique, voyons maintenant les fonctions internes (ou nested function) ! En JavaScript il est possible de définir une fonction à l »intérieur d »une autre fonction. L »intérêt principal est de profiter pleinement de l »encapsulation. En effet, la fonction internet sera une fonction « privée », accessible uniquement à partir de sa fonction parente. Cela signifie qu’2014-02-20 17:02:36’elle aura accès aux variables globales, aux variables de sa fonction parente et à ses variables qui n »existeront plus à l »extérieur de son bloc. Lorsqu »on crée une fonction interne on crée donc un « scope » (cela est en réalité bien plus compliqué mais nous y reviendront en détail dans le billet suivant). Ce que j »entends par le mot « scope »,  c »est un espace dans lequel les variables définies existent. En dehors de cette espace, elles sont inconnues au bataillon. Les fonctions internes sont donc utiles pour découper son code et effectuer des traitements internes et spécifiques à une fonction. Si on essaye d »accéder à une fonction interne depuis l »extérieur de la fonction parente, on aura une erreur « nomDeLaFonctionInterne is not defined ». Voici un exemple de fonction interne :

function maFonction(x) {
  function maFonctionInterne(x) {
    return x*x;
  }
    alert("Résultat : " + maFonctionInterne(x));
}

maFonction(10);

Abordons maintenant les fonctions anonymes auto-exécutantes (ou encore self-executing anonymus function). L »usage principal de ce type de fonction est d »isoler une partie de son code afin d »éviter par exemple d »écraser des variables précédemment définies. L »auto-exécution s »effectue par l »ajout d »une parenthèse ouvrante et fermante à la fin de la définition de la fonction :

(function () {
    alert('auto-exécution');
})();

Comme vous pouvez le voir, à la fin du chargement de la page la fonction s »exécute instantanément ! Cela revient exactement au même que cette ligne :

 alert('auto-exécution');

L »intérêt des fonctions anonymes auto-exécutantes est donc le même que pour les fonctions internes : définir un « scope ». Les variables définies dans ce « scope » ne seront pas accessibles à l »extérieur, en revanche ce « scope » a toujours accès aux variables globales ! Cela permet de ne pas écraser des variables globales déjà existantes, en les redéfinissant dans sa fonction auto-exécutante. Un exemple en dit plus qu »un long discours :

var maVariableGlobal = 42;

(function () {

    //Ici on n'écase pas la variable crée dans le scope parent
    var maVariableGlobal = "maChaine";
    //Cette variable ne sera pas accéssible à l'éxterieur du bloc
    var maVariable = 42;
    alert(maVariableGlobal);

})();
alert(maVariableGlobal);
alert(maVariable); //Ici message d'erreur : maVariable is not defined

Pour qu »une fonction auto-exécutante manipule et affecte des variables en dehors de son scope il y a plusieurs solutions : les paramètres, les valeurs de retour et les variables globales. Une variable non déclarée à l »aide du mot « var » est automatiquement rattachée au global object : window. On peut donc créer des variables depuis la fonction auto-exécutante et les affecter à des propriétés de l »objet windows. Ces propriétés seront accessibles dans toute l »application. On peut également passer des paramètres à la fonction, et utiliser sa valeur de retour pour, par exemple, l »affecter à une variable globale de l »application. Voici un exemple :

var maVariableGlobal = 42;

var maVariableGlobal2 = (function (x) {

    var maVariablePrivee = 2;
    window.maPropriete = "maChaine";
    return maVariablePrivee * x;

})(maVariableGlobal);

alert(maVariableGlobal);
alert(maVariableGlobal2);
alert(maPropriete);

La syntaxe des fonctions auto-exécutantes doit d »ailleurs vous rappeler quelque chose : la libraire Jquery ! Lorsqu »on utilise l »objet Jquery au sein d »une fonction, on utilise ce mécanisme afin d »utiliser le $ plutôt que de taper Jquery à chaque fois :

(function ($) {
  $("body").append("ok");
})(jQuery);

Cette façon de procéder est par exemple utilisée pour la librairie JQuery UI, allez jeter un œil dans les sources !  

Avant d »aborder les fermetures, j »aimerai revenir sur un mécanismes assez déroutants pour les non afficionados du js : le hoisting. Ce phénomène permet de mieux comprendre la portée des variables en js. Il faut bien comprendre qu »en js contrairement au C, C++, C#,… Un scope est créée à chaque fonction et non à chaque bloc. Dans l »exemple suivant la variable b, créée dans un bloc, est accessible à l »extérieur de ce bloc ;en revanche la variable c n »est définie qu »à l »intérieur de la fonction d »où l »erreur « c is not defined » : 

var a = 1;
if(true)
{
  var b=2;   
}


function maFonction() {
    var c = 3;
}
alert(a);
alert(b);
alert(c);

Maintenant que la création d »un scope est claire pour vous, voyons voir un exemple qui met en scène le mécanisme d »hoisting :

var a = 1;
var b = 2;

function maFonction() {
    alert(a);
    alert(b);
    var a = 5;
    alert(a);
}

maFonction();

Pourquoi donc le premier alert affiche-t-il « undifined » ???? Hoisting signifie hisser, et ici c »est exactement ce qu »il se passe. La  déclaration de la variable « a » est en fait automatiquement hissée au début de la fonction, mais son assignation reste là où nous l »avons mise, c »est à dire après le premier alert. La variable n »a donc pas de valeur assignée au moment du premier alert. Afin que vous compreniez bien ce qu »il se passe, regardez attentivement l »exemple ci-dessous : 

function maFonction() {
    alert(a);
    var a = 5;
    alert(a);
}
/*Ces 2 fonctions sont exactement les même*/
function maFonction2() {
    var a;
    alert(a);
    a = 5;
    alert(a);
}
maFonction();
maFonction2();

A partir de l »exemple ci-dessus vous comprenez mieux pourquoi le premier alert affichait undifined. Puisque les variables définies dans une fonction sont automatiquement déclarées au début de cette fonction, il ne faut donc les utiliser que lorsqu »elles ont été initialisées ! Les fonctions sont également touchées par le hosting, voici un exemple mettant en scène le phénomène :

function maFonction() {
    fonction2();
    fonction1();
    var fonction1 = function () {
        alert("Ok");
    }

   function fonction2() {
            alert("Ok");
        }
}
maFonction();

Dans cette exemple, seul le nom (la variable à laquelle elle est affectée) de la fonction1 est « hisser », alors que pour la fonction2 le corps de la fonction est également hisser. Cela explique que le premier « ok » s »affiche, puis l »appel à la fonction1 donne une erreur car la variable n »a pas encore été assignée. Ce qu »il faut principalement retenir de ce mécanisme est de toujours prévoir exactement une déclaration par scope (par fonction) et de surtout tout déclarer dès le début de la fonction afin de ne pas accéder à des variables non initialisées.

Dans ce billet nous avons vu comment découper son code plus proprement avec les fonctions internes et les fonctions auto-exécutantes. Tout au long de ce billet j »ai employé le terme « scope » par abus de langage, dans le billet suivant nous entrerons en détail dans la création d »un scope lexical pour ensuite passer à un élément clé en Javascript : les fermetures !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.