La fonction anonyme appelée immédiatement en Javascript: (function())()


Javascript est un langage qui a plein d’idiomes bien à lui. En effet, c’est un langage très puissant, et aussi plein de couilles velues planquées ici et là. Les ninjas JS ont donc créée des astuces pour pallier à ces problèmes, en utilisant la force de leur outil.

Un des gros soucis en JS, c’est qu’il dépend beaucoup des variables globales, qui sont une grosse source d’ennuis en tout genre. Et il est très facile de déclarer ou d’utiliser une variable globale par erreur.

Pour limiter ce problème, on utilise la technique de la fonction anonyme immédiatement appelée. Cela fonctionne comme ceci, au lieu de faire:

    alert('rouge');

On fait:

(function(){
    alert('rouge');
})()

Explication pas à pas

En effet, en Javascript on peut créer une fonction sans lui donner de nom. C’est ce qu’on appelle une fonction anonyme:

function(){
    alert('rouge');
}

On peut attribuer cette fonction à une variable.

var une_variable_qui_passait_par_la = function(){ alert('rouge')};

Et derrière on peut appeler la fonction ainsi:

une_variable_qui_passait_par_la();

Mais dans notre cas, on ne met pas la fonction dans une variable, on l’enferme dans des parenthèses, ce qui garde la référence le temps de l’appeler, et on l’appelle. On fait la même chose que précédement en fait, on saute juste une étape :

(function(){ alert('rouge');})()

La référence à la fonction est perdue, on ne pourra pas l’appeler de nouveau. Mais on s’en branle fortement, on voulait juste l’appeler une fois de toute façon.

Pour rendre le code plus lisible, on le met sur plusieurs lignes:

(function(){
    alert('rouge');
})() /* on éxécute la fonction cash pistache */

Maintenant pour comprendre ce que ça fait, il faut juste réaliser que le bout de code ci-dessous fait EXACTEMENT la même chose que le bout de code juste au dessus :

alert('route');

Car on déclare la fonction, mais on l’éxécute tout de suite. Donc dans les deux cas, au chargement du script, un aura une alerte immédiatement.

Mais putain si ça fait la même chose, pourquoi tu nous gonfles avec ça ?

Parceque dans le cas du dessus ça fait la même chose, mais ça a des propriétés en plus. Vous vous doutez bien qu’on tape pas 4 lignes en trop pour le plaisir (à part en Java).

En effet, en créant une fonction, on crée un scope. Ainsi, tout ce qui est déclaré avec var dans la fonction sera garanti de ne pas être une variable globale. Pas d’effet de bord sur le monde extérieur. Et le monde extérieur n’a PAS accès aux variables déclarées dans la fonction. Par contre la fonction a accès aux variables globales !

Par exemple, dans un navigateur, vous avez toujours accès à l’objet window et l’objet Date partout dans le code.

/*
    Ici vous avez accès à window et Date
*/
 
(function(){
 
    /* Ceci ne va pas changer l'objet window en dehors de la fonction
        car on redéclare la variable, à l'intérieur du scope de la fonction.
     */
    var window = '3.1';
 
    var rideau = 'vista';
 
    /* Ici vous avez accès à Date */
 
    alert(new Date())
 
 
})()
 
/*
    Ici vous n'avez PAS accès à "rideau"
*/

Il est donc une bonne pratique de mettre TOUT son script dans une fonction qui s’auto-appelle, pour l’isoler du monde exterieur et ne pas pourrir le namespace global. Si on avait pas fait ça, et qu’un autre script définissait aussi rideau, l’un des deux aurait écrasé l’autre. Et dans tous les cas on aurait écrasé window.

Encore mieux

Comme on crée un conteneur isolé, on peut faire ce que l’on veut dedans. Et on a deux moyens de communique avec ce conteneur. D’abord, les variables globales. Ensuite, les paramètres de la fonction auto-appelée.

On peut exposer le contenu de la fonction à travers les variables globales. Par exemple, vous faites un générateur de ‘pouet’, chose importante s’il en est :

(function(){
 
    /* ces variables ne sont pas accessibles depuis l'extérieur */
    var pouetGenerator = {};
    var variablePrivee = true;
 
    /* ces fonctions sont accessibles seulement depuis pouetGenerator */
    pouetGenerator.fairePouet = function(){
        alert('pouet')
    }
 
    pouetGenerator.nePasFairePouet = function(){
        if (variablePrivee) {
            alert('pas pouet');
        }
    }
 
    /* On rend notre pouetGenerator accessible au reste du monde */
    window.pouetGenerator = pouetGenerator;
 
})()
 
/* On peut utiliser notre générateur de pouet partout ailleurs */
pouetGenerator.nePasFairePouet()

En effet, tout ce qui est attaché à window devient une variable globale dans un navigateur. C’est une caractéristique de Javascript dans un navigateur Web: window est un objet accessible partout, et on a accès partout à ses attributs. On a donc notre code parfaitement isolé (la seule chose exposée c’est notre point d’entrée : pouetGenerator). On ne pollue pas le namespace. En prime personne n’a accès aux variables privées comme variablePrivee;

On peut aussi utiliser les paramètres et les valeurs de retour pour cela :

/* pouetGenerator, déclaré sans "var", est une variable globale */
pouetGenerator = (function(chaine_du_pouet){
 
    /* ces variables ne sont pas accessibles depuis l'extérieur */
    var pouetGenerator = {};
    var variablePrivee = true;
 
    /* ces fonctions sont accessibles seulement depuis pouetGenerator */
    pouetGenerator.fairePouet = function(){
        alert(chaine_du_pouet)
    }
 
    pouetGenerator.nePasFairePouet = function(){
        if (variablePrivee) {
            alert('pas pouet');
        }
    }
 
    /* On rend notre pouetGenerator accessible au reste du monde
        en la retournant, ce qui va mettre la référence dans le pouetGenerator
        qui est la variable globale.
    */
    return pouetGenerator;
 
/* On passe 'pouet' en paramètre, il va se retrouver dans chaine_du_pouet */
})('pouet')
 
/* On peut utiliser notre générateur de pouet au même niveau */
pouetGenerator.nePasFairePouet()

Il y a deux différences dans ce code. La première, c’est qu’on met la variable à disposition en la retournant plutôt qu’en l’attachant à document. La seconde, c’est qu’on passe 'pouet'.

On pourrait se dire que l’interêt est limité, mais considérez l’exempe suivant:

(function($){
    $('p').css('color', 'rouge-fluo')
})(jQuery.noConflic())

jQuery.noConflic() restaure $ à sa valeur précédente, permettant à d’autres libs d’utiliser $ (comme la lib prototype.js). Mais jQuery.noConflic() retourne aussi l’objet jQuery, que l’on passe en paramètre. Ce paramètre est nommé $ dans la signature de la fonction. Dans notre fonction, on peut donc QUAND MÊME utiliser $ pour faire référence à jQuery car tout ce qui est dans la fonction est isolée du reste du monde. Et le reste du monde peut utiliser $ pour autre chose que jQuery.

Best practice

Généralement un code de production pour un script complet ressemble donc à ça:

;(function(alias){
"use strict";
 
/* tout le code du script va ici */
 
var une_variable_privee;
 
window.une_variable_exposee = 'foo';
 
})(truc_a_aliaser)

Notez le ; au début qui permet de rajouter votre script à la suite d’un autre script même si celui-ci a oublié de mettre un ; sur sa dernière ligne (utile pour les minifieurs).

Notez aussi le "use strict"; qui dit au navigateur que tout ce qui se trouve dans cette fonction devra être traité par un parseur strict qui vous signalera plus d’erreurs de code. Cela ne s’applique qu’à la fonction, laissant le reste du monde le droit d’utiliser du code moins strict.

18 thoughts on “La fonction anonyme appelée immédiatement en Javascript: (function())()

  • Ghusse

    Je je ne m’abuse, l’accès aux variables globales en javascript se fait avec window et non document.

  • CrEv

    Juste une petite erreur dans le dernier code :

    ;(function(alias){
    "use strict";
     
    /* tout le code du script va ici */
     
    var une_variable_privee;
     
    document.une_variable_exposee = 'foo';
     
    }(truc_a_aliaser)

    Il manque la parenthèse fermante, ça devrait être :

    ;(function(alias){
    "use strict";
     
    /* tout le code du script va ici */
     
    var une_variable_privee;
     
    document.une_variable_exposee = 'foo';
     
    })(truc_a_aliaser)
  • Sam Post author

    @Ghusse Wooops. Je confond toujours les deux donc je test chaque fois dans la console quand je code, et j’oublie.

    @CrEv: bien vu, fixed.

    @desfrenes: oui et c’est même assez malin: c’est juste une string, donc ignorée par les navgateurs incompatibles, mais pris en compte au besoin. Ca permet d’avoir un javascript moins con.

  • anon

    J’aime quand on parle de JS.

    Mais je suis déçu par le manque d’illustration, votre esprit tordu serait il a court de ressources?

  • roro

    J’apprécie le “pouet” qui déconfuse l’esprit.
    Et avec un morceau pareil c’était pas du luxe.

  • Ghusse

    En fait, ça active des vérifications supplémentaires par le navigateur.

    En gros, ça lève plus souvent des exceptions quand on fait des trucs pas nets.

  • Sam Post author

    @defresnes: C’est vrai que ça mérite un article.

    @anon: c’est surtout que cette salope de max ne branle rien sur le blog en ce moment, et comme c’est le plus pervers de deux, on perd en cochoneries. Il faut faire pression sur lui. Max ! Laisse deux minutes ton optimisation des screenshots anals et viens poster un article avec de la moule un peu !

  • Kontre

    Ah, les fonctions anonymes, ça me rappelle le langage Lua où je n’utilisais presque que ça pour les callbacks. C’est mon plus gros manque en python, il faut déclarer plein de fonctions pour les interfaces, beaucoup de blabla pour pas grand chose (souvent les callbacks ne font que 2 ou 3 lignes).
    Les lambdas, c’est tellement naze en comparaison…

  • Sam Post author

    Let ne marche que dans les browsers qui supportent javascript version 1.7, ce qui exclut IE et est opt-in chez chrome il me semble.

  • Greg

    sympa de lire du JS pour changer :)
    J’avais lu que Facebook utilisait souvent la notation !function(){} au lieu des parentheses englobantes. Je me suis dit a l’epoque que c’etait juste pour faire les malins, mais la je me rends compte que ca sautera plus facilement au yeux que des parentheses, qui peuvent deja englober une fonction anonyme quand on passe un callback par exemple.
    On peut aussi en faire pas mal d’autres, des syntaxes “here comes an IIFE”. J’aime bien le tilde que je n’utilise jamais en dehors.
    -function(){}
    +function(){}
    ~function(){}

  • Goldy

    C’est une méthode très intéressante pour l’application que je suis en train de développer. Merci pour l’article.

  • Colegos

    Un cour intéressant et très simple, j’avais deja utilisé plusieurs fois ce genre “d’encapsulation” de fonction mais sans en connaitre l’utilité… Aujourd’hui c’est chose faite grâce à vous !
    Merci pour ce cour et j’aime bien le coté bas les couilles du chroniqueur, je vais le suivre avec ses autres articles et me remettre dans le html_5 et le tout le Js qui s’en suis !

  • *megaten

    Putain, mais oui bon sang…

    Il fallait bien cela et quelques relectures pour en saisir tout l’interêt. J’ai pas dit comprendre et maitrisé.

    Merci

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.