javascript-pour-les-jedis
JavaScript

JavaScript pour les Jedis, épisode I : Au coeur des fonctions

Comme vous le savez tous, 2014 c’est l’année de JavaScript. C’est pour cette raison que j’ai décidé de me consacrer à la rédaction d’une série d’articles concernant les fondamentaux de JavaScript, un langage encore méconnu. Ces billets auront pour but de vous expliquer en détails le fonctionnement de ce langage. Nous verrons comment JavaScript gère les fonctions et les fermetures (closures) ; nous apprendrons aussi la programmation orientée objet grâce aux prototypes ; sans oublié d’autres aspects tels que les expressions régulières, les événements (même si cela est plus lié au DOM), les timers…Bref ! nous allons explorer en détails tout ce qui touche à ce merveilleux langage.

Cela bien évidement dans le but de faire de vous des JavaScript Jedis, ou des JSJ !

Le but étant pour moi de vous aider à apprendre et comprendre JavaScript afin de tirer le meilleur de ce langage. A vrai dire, j’en ai un peu marre de voir beaucoup de développeurs “obligés” de coder du JavaScript dans leur projet, traiter ce langage de tous les noms car ils n’arrivent pas à faire ce qu’ils ont l’habitude de produire avec leurs langages favoris. Et souvent, cela vient du fait que ces personnes ne prennent même pas la peine d’apprendre le langage. Il finissent donc par développer une sorte de haine contre ce langage…

“JavaScript, tu l’apprends ou tu le quittes” — de moi ^^

Vu le titre de ce premier épisode, vous vous demandez surement pourquoi nous commençons cette série par les fonctions, et non pas par les objets. Eh bien! Etant donné la nature fonctionnelle de JavaScript, pour moi, maîtriser cet aspect du langage c’est ce qui fera de vous des Jedis. En effet, la plus part des personnes venant d’autres langages orientés objet, essayent plus de reproduire des paradigmes propres à ces langages et font l’impasse sur les fonctions et les fermetures (closures). JavaScript est composé de ces trois concepts : les objets, les fonctions et les closures. Maîtriser JavaScript passe par l’apprentissage de cet aspect fonctionnel du langage ; Et croyez-moi, le niveau de sophistication du code que vous écrirez dépend de cet apprentissage.

Commençons donc sans plus tarder…

JavaScript est un langage fonctionnel

L’une des raisons qui fait que les fonctions et la nature fonctionnelle de JavaScript sont des concepts très importants, vient du fait que “la fonction” est le premier module d’exécution en JavaScript. C’est-à-dire que lorsque votre code est exécuté, il l’est au sein d’une fonction ; à l’exception des scripts qui sont interprétés lorsque le code HTML est en train d’être évalué par le navigateur.

Une chose très importante à connaitre sur les fonctions en JavaScript, ce sont des objets de premier ordre (first-class functions). Ceci veut dire que les fonctions sont traitées comme des objets, elles peuvent donc être :

  • crées via des littérales ;
  • assignées à des variables ou propriété d’un objet ;
  • passées en paramètre  ;
  • retournées comme résultat ;
  • elles peuvent également posséder des objets ou méthodes.

En plus d’être traitées comme des objets, les fonctions ont également cette capacité spéciale : elles peuvent être invoquées (on en reparlera plus en détails dans la suite de cet épisode). Cette invocation est souvent effectuée de manière asynchrone. Voici pourquoi…

La boucle d’événements

Si vous avez déjà codé une interface graphique auparavant, vous avez surement procédé comme ceci :

  1. mettre en place l’IHM ;
  2. se mettre dans une boucle d’événements en attente qu’un événement se produit ;
  3. invoquer les actions à exécuter pour chaque événement.

La programmation en JavaScript — dans un navigateur — est quasiment similaire, sauf que la gestion des événements est totalement prise en charge par le navigateur. Nous avons juste besoin de spécifier les actions, ou les listeners, pour les différents événements qui peuvent être déclenchés (toujours au sein du navigateur). Ces événements sont placés dans une FIFO au fur et à mesure qu’ils se produisent, et le navigateur traite ces événements en invoquant les listeners associés.

Puisque ces événements peuvent se produire à n’importe quel moment et dans n’importe quel ordre, la gestion de ces événements et donc l’invocation de leurs listeners se fait de manière asynchrone. Pourquoi me diriez-vous ?

Une chose à savoir, la boucle d’événements du navigateur est “mono-threadée”, ce qui veut dire que chaque événement se trouvant dans la file d’attente, est traité (dans l’ordre). C’est ce que l’on appelle une FIFO. Les événements sont donc traités un par un, à tour de rôle. Voici un schéma très simplifié de ce processus :

 

javascript-pour-les-jedis-episode-1-au-coeur-des-fonctions-boucle-événements-navigateur

Un exemple typique du fonctionnement de cette boucle : lorsque vous bougez le curseur de votre souris (dans votre page web), le navigateur détecte ces déplacements de souris et place cette série d’événements mousemove dans la FIFO. Ensuite, la boucle d’événements s’occupera du traitement de ces événements, en exécutant les listeners associés.

Ce principe de listeners — associer une fonction qui sera exécutée plus tard, lorsque l’événement se produira — illustre un mécanisme que l’on appelle les fonctions de callback.

Le principe de callback

Les fonctions de callback sont une partie très essentielle dans la bonne compréhension de JavaScript. Explorons ce point…

Dans la partie précédente, nous avons dit que l’on pouvait associer des listeners aux différents événements se produisants au sein du navigateur. Voici un exemple de ces listeners :

function executerAuChargement(){ /* ... */ };
window.onload = executerAuChargement;

Ici, nous avons attaché un listener qui sera exécuté lorsque la page est complètement chargée par le navigateur. Parce que les fonctions sont des first-class object, nous pouvons affecter cette fonction à la propriété (de type événement) onload  de l’objet window (représentant le navigateur).

Aussi, nous avons dit que les fonctions peuvent être passées en paramètre. Ce qui veut dire que l’on peux écrire ceci :

function dire(callback){
    alert(callback())
}
function bonjour(){
    return "Bonjour, JavaScript!";
}
function executerAuChargement(){
    return dire(bonjour)
};
window.onload = executerAuChargement;

Voila un exemple intéressant ! Nous pouvons aussi appeler les fonctions de callback dans nos propres fonctions, pas besoin qu’elles soient associées à des événements. Dans cet exemple, nous avons attaché une fonction de callback à l’événement onload. Lorsque la boucle d’événement exécutera ce listener, à son tour il exécutera la fonction dire à laquelle nous avons passé la référence vers la fonction bonjour. Une fois exécutée, la fonction dire invoquera la fonction bonjour ce qui provoquera l’affichage du message “Bonjour, JavaScript!”.

Mieux encore ? La nature fonctionnelle de JavaScript nous permet de créer des fonctions en tant qu’entité à part entière, comme n’importe quel autre type, et les passer directement en paramètre. Voyons un exemple :

function dire(callback){
    alert(callback())
}
function executerAuChargement(){
    return dire(function(){
        return "Bonjour, JavaScript!";
    })
};
window.onload = executerAuChargement;

Nous avons modifié l’exemple précédent en supprimant la déclaration de la fonction bonjour. Nous l’avons remplacé par la déclaration d’une fonction sans nom, directement dans les paramètres de la fonction dire. En JavaScript, nous appelons cela les fonctions anonymes.

Les fonctions anonymes

Etant donné la nature fonctionnelle du langage, il possible de créer des fonctions n’importe où dans le code — là où une expression est attendue. Cela a pour avantage de rendre le code plus claire, plus compact et en plus cela permet d’éviter de polluer l’espace de nom globale avec des noms de fonctions inutiles.

En effet, une fonction anonyme est une fonction — comme les autres — déclarée en “in-line”, et qui n’a pas de nom pour la référencer. Ces fonctions anonymes sont généralement déclarées au moment où elles sont référencées.

Pourquoi donner un nom à un chat, s’il ne réagit pas lorsqu’on l’appelle ?

Généralement en JavaScript, lorsque nous estimons qu’une fonction va être référencée dans plusieurs endroit du code, nous lui donnons un nom. Sinon, si elle est destinée à servir juste à un seul endroit, alors pas besoin qu’elle ait un nom.

Ce qui nous amène aux déclarations et invocations des fonctions.

Déclaration de fonctions

En JavaScript, les fonctions sont déclarées à l’aide du mot-clé function qui crée une valeur de type fonction. Rappelez-vous que les fonctions sont des objets de premier ordre, elles peuvent être manipulées dans le langage comme n’importe quel autre type. De ce fait, il nous est possible d’écrire ceci (en reprenant l’exemple précédent) :

var dire = function(callback){
    alert(callback())
}

La valeur de la variable dire est une fonction ! Nous avons donc déclaré et crée une fonction dont le nom est dire.

Toutes les fonctions déclarées ont une propriété nommée name  qui contient le nom de la fonction (en chaine de caractères). Pour les fonctions anonymes, cette propriété est présente mais contient une chaine de caractère vide.

Lorsqu’une fonction a été déclarée avec un nom, ce nom reste valide dans tout le contexte dans lequel cette fonction a été déclarée. Aussi, si cette fonction a été déclarée dans le contexte globale, une nouvelle propriété portant le nom de la fonction est ajouté dans le window (ceci est également vrai pour les variables, mais nous verrons cela dans de prochains épisodes).

Ceci nous amène donc à la suite de cet épisode concernant le contexte et la portée des fonctions.

Portée et contexte(s) d’une fonction

Lorsque nous déclarons une fonction, nous devons avoir à l’esprit, non seulement le contexte dans lequel cette fonction existe, mais également quels sont le ou les contextes que cette fonction crée. Nous devons également faire très attention aux déclarations faites au sein de ces contextes.

La gestion des portées en JavaScript est très différentes de la plus part des autres langages dont la syntaxe a été influencée par le langage C. Plus précisément, ceux utilisant les accolades { }  pour délimiter la portée des variables dans les blocs. Dans ces langages, chaque bloc crée son propre contexte. Ce n’est pas le cas de JavaScript.

En JavaScript, les contextes sont crées par les fonctions, et non pas par les blocs. La portée d’une variable crée au sein d’un bloc continue d’exister en dehors de ce dernier (contrairement aux autres langages). Par exemple :

if(true){
    var foo = 'bar';
}
console.log(foo); // 'bar'

Ce simple exemple prouve bien que la portée de la variable foo est globale, elle n’est pas liée au bloc if.

Regardons un exemple un peu plus intéressant :

function fonction1(){
  var a = 1;
  function fonction2(){ /**/ }
  var b = 2;
  if (a == 1) {
    var c = 3;
  }
}
fonction1();

Dans cet exemple, nous avons déclaré deux fonctions,  fonction1 et fonction2, ainsi que trois variables, a, b et c. Pour compléter cet exemple, voici un schéma plus parlant qui explique les différents contextes crées :

javascript-pour-les-jedis-episode-1-au-coeur-des-fonctions-contexte-et-portée

Ce schéma résume les règles suivantes :

  • la portée d’une variable existe depuis sa déclaration et ce jusqu’à la fin de la fonction dans laquelle elle a été déclarée (peu importe l’imbrication des blocs) ;
  • la portée d’une fonction (non-anonyme) est globale à toute la fonction dans laquelle elle a été déclarée (on appelle cela hoisting, ou hissage en français).

Cette deuxième règle nous montre que, contrairement aux variables, les fonctions peuvent être référencées avant leurs déclarations ! Oui, oui, c’est bien ça, vous pouvez invoquer function1() avant de la déclarer. Par contre, ce n’est pas une bonne pratique. Ce n’est pas parce que c’est autorisé par le langage qu’il faut le faire.

Maintenant que nous avons vu comment les fonctions sont déclarées, essayons de voir comment celles-ci peuvent être invoquées. Vous allez voir, cela risque d’être très amusant ^^

Invocation des fonctions

Nous avons tous invoqué des fonctions en JavaScript. Mais avez-vous déjà essayé de comprendre ce qui se passe lorsque vous invoquez une fonction ? Dans cette partie nous allons nous intéresser aux différentes façons d’invoquer une fonction en JavaScript.

Il existe quatre façons d’invoquer une fonction en JavaScript. Chaque type d’invocation a un effet direct sur le code exécuté au sein de cette fonction. Plus précisément, le type d’invocation agit sur le paramètre this passé à la fonction lors de son invocation. Ecrire du code digne d’un Jedi, repose sur la compréhension et la bonne maîtrise de ces mécanismes d’invocations.

Comme je l’ai mentionné, il existe quatre types d’invocation en JavaScript :

  1. en tant que fonction (c’est le cas le plus répandu) ;
  2. en tant que méthode, ce qui permet de lier l’invocation à un objet (POO) ;
  3. en tant que constructeur, ce qui permet de créer un objet (instance) ;
  4. via apply() et call() (nous verrons cela dans la suite de cet article).

Avant d’expliquer chaque type d’invocation, prenons quelques minutes et essayons de comprendre ce qui se passe lors d’une invocation de fonction (cela est vrai pour les cas 1, 2, et 3).

Arguments et paramètres

Lorsqu’une liste d’arguments est fournie lors d’une invocation de fonction, ces arguments sont affectés aux paramètres de la fonction spécifiés lors de la déclaration de la fonction, dans le même ordre. Jusque là rien de bien sorcier.

Maintenant, si le nombre d’arguments est différent de celui des paramètres, aucune erreur n’est déclenchée : JavaScript sait comment traiter ce cas. Voici comment :

  • s’il y a plus d’arguments que de paramètres, alors l’excédent est ignoré ;
  • s’il y a moins d’arguments que de paramètres, alors les paramètres qui n’ont pas d’arguments seront mis à undefined .

De plus, au moment de l’invocation, deux paramètres additionnels sont passés implicitement :this et arguments. Autrement-dit, ces deux paramètres sont passés à la fonction qui est invoquée, même s’ils ne sont pas spécifiés en tant que paramètres dans la déclaration. Ils sont par la suite disponibles dans le corps de la fonction et peuvent être référencés comme n’importe quels autres vrais paramètres.

L’argument “arguments”

Cet objet est une sorte de collection de tous les arguments passés à la fonction.

function foo(){ 
    console.log(arguments.length); // 3
    console.log(arguments); // [1,2,"bar"]
    console.log(arguments[2]); // "bar"
    console.log(arguments.filter); // undefined
}; 
foo(1,2,'bar');

Mais attention, même si cet objet possède un attribut length , et permet d’accéder aux arguments via une notation similaires aux tableaux arguments[1] , ce n’est pas un tableau à proprement parlé. Vous ne pouvez donc pas utiliser les méthodes spécifiques aux tableaux, map() ou filter() par exemple.

L’argument “this”

Cet argument est un peu particulier. Il représente l’objet qui est associé — de façon implicite — à l’invocation de la fonction. Il est aussi connu sous le terme de contexte. Le terme contexte est bien connu de ceux qui viennent du développement orienté objet. Cependant, dans la majorité des cas, ces mêmes personnes pensent qu’en JavaScript, le mot clé this représente l’instance de la classe dans la quelle la méthode est définie. FAUX !

En JavaScript, il se trouve que le contexte représenté par le paramètre this est conditionné par la façons dont la fonction a été invoquée, et non pas par la façon dont elle est définie. C’est pour cela que ce paramètre est appelé le contexte d’invocation.

Maintenant que nous avons bien compris ce que c’est this  et ce qu’il représente. Passons aux différents type d’invocations.

Invocation en tant que fonction

Ce type d’invocation est surement le plus utilisé. En effet, si une fonction n’est pas invoquée en tant que méthode, constructeur ou via apply()  ou call(), alors nous sommes en présence d’invocation d’une fonction.

Cette invocation se produit lorsque la fonction est invoquée en utilisant les parenthèses (args) et lorsque cette fonction n’est pas une propriété d’un objet (dans ce cas, nous sommes en présence d’une invocation de méthode). Voici un exemple plus parlant :

function foo(){};
foo();

var bar = function(){};
bar();

Lorsqu’il est invoqué de cette manière, le contexte d’invocation de ces fonctions est window, le contexte globale en JavaScript.

Invocation en tant que méthode

Lorsqu’une fonction est une propriété d’un objet et que l’invocation se fait en appelant cette fonction, alors la fonction est invoquée en tant que méthode de cet objet. Pour illustrer mes propos, prenons cet exemple :

function foo(){ return this; }

// invocation en tant que fonction
foo(); // window

var bar = {
    'foo': foo
};

// invocation en tant que méthode de 'bar'
bar.foo(); // bar

Nous avons déclaré une fonction foo  que nous invoquons de deux manières : en tant que fonction, puis en tant que méthode de l’objet bar. Nous constatons qu’en effet, le contexte d’invocation retourné (représenté par this ), est différent dans les deux cas. Quand invoquée en tant que fonction, this représente le contexte globale window ; et lorsque la fonction est invoquée en tant que méthode, this représente l’objet hôte de la méthode au moment de l’invocation. Cette particularité prend tout son sens en programmation orienté objet en JavaScript (qui sera discutée dans un prochain épisode).

Invocation en tant que constructeur

Un constructeur est une simple fonction, il n’a vraiment rien de spécial. La seule différence réside dans la façon dont elle va être invoquée. Pour invoquer une fonction en tant que constructeur, nous le précédons par le mot-clé new.

Invoquer une fonction en tant que constructeur est un aspect très intéressant de JavaScript, car lorsqu’un constructeur est invoqué, plusieurs choses se produisent :

  • un nouvel objet vide est crée ;
  • cet objet est passé au constructeur (le fameux paramètre this). Il devient donc le contexte d’invocation ;
  • En l’absence d’une instruction return explicite dans le constructeur, cet objet est retourné par le constructeur.

Prenons cet exemple :

function Foo(){
    this.bar = function(){
        return this;
    }
}
// exemple 1
var foo = new Foo();
console.log(foo); // Foo
console.log(foo.bar() instanceof Foo); // true

// exemple 2
var foo1 = Foo();
console.log(typeof foo1); // undefined
console.log(typeof window.bar); // function

Nous avons déclaré un constructeur Foo (notez la première lettre en majuscule), celui-ci déclare une propriété bar, qui est une méthode retournant le contexte this (pour l’exemple).

Dans un premier temps, nous invoquons ce constructeur avec le mot-clé new et nous avons bien un objet de type Foo qui a été crée, et qui possède bien une méthode bar. Un petit test sur le contexte this dans foo, nous prouve bien que c’est une instance de Foo.

Dans l’exemple 2, nous avons essayé de démontré qu’invoquer un constructeur en tant que fonction (sans le mot-clé new) ne donne pas du tout le résultat attendu. En effet, invoquer Foo en tant que fonction, exécute simplement le code de cette fonction, ce qui provoque la création de la propriété bar  dans l’objet hôte — dans lequel Foo a été invoquée. Cet objet représenté par this  dans Foo est bien évidement window, puisque le fonction Foo a été déclarée dans le contexte global de JavaScript.

Pour résumer, invoquer un constructeur en tant que fonction est juste inutile!

Jusque là, nous avons vu que le type d’invocation d’une fonction agit directement sur le contexte d’invocation, représenté par le paramètre implicite this. Pour les méthodes c’est l’objet hôte ; pour les fonctions c’est le window (nous parlons bien des fonctions déclarées dans le contexte global) ; et pour les constructeurs c’est l’instance du nouvel objet crée.

Maintenant, comment pouvons-nous faire si nous voulons forcer un contexte d’invocation particulier ? C’est bien grâce à apply() et call(). Regardons cela de près.

Invocation via apply() ou call()

JavaScript offre une méthode simple, pour invoquer une fonction et lui spécifier explicitement un contexte d’invocation. Nous pouvons réaliser cela grâce à deux méthodes proposées par toutes les fonctions : apply() et call().

Pourquoi voudrions-nous faire cela ? Prenons l’exemple d’une situation que nous rencontrons régulièrement en JavaScript :  les listeners — ou callbacks — des événements déjà abordés plus haut. Lorsque nous déclarons une fonction de callback sur un événement, celle-ci est invoquée lorsque l’événement en question est dispatché par le navigateur. Cette fonction de callback est invoquée avec le contexte de l’événement, c’est un comportement par défaut.

function foo(){
    console.log(this); // this === window
    console.log(arguments); // a, b, c
}
var element = document.querySelector('#foo');
element.addEventListener('click', function(e){
    console.log(this); // element
    console.log(arguments); // e === Event

    foo('a', 'b', 'c');
});

Comme l’illustre le code ci-dessus, le contexte d’invocation de la fonction de callback — représenté par this — est celui de window ; la callback foo est une fonction déclarée dans le contexte globale. Elle a été invoquée en tant que fonction.

Voyons maintenant le même code mais cette fois-ci en utilisant call().

function foo(){
    console.log(this); // this === element
    console.log(arguments); // a, b, c
}
var element = document.querySelector('#foo');
element.addEventListener('click', function(e){
    console.log(this); // element
    console.log(arguments); // e === Event

    foo.call(element, 'a', 'b', 'c');
});

En invoquant une fonction — ou méthode — avec  call(),  nous passons un premier paramètre qui représente le contexte d’invocation. Dans notre exemple, ce contexte est l’élément DOM qui nous intéresse. En plus du contexte, nous avons aussi passé une liste de paramètre à la fonction foo.

function foo(){
    console.log(this); // this === element
    console.log(arguments); // a, b, c
}

var element = document.querySelector('#foo');
element.addEventListener('click', function(e){

    console.log(this); // element
    console.log(arguments); // e === Event

    foo.apply(element, ['a', 'b', 'c']);
});

Avec apply(), c’est quasiment la même chose : nous passons également le contexte d’invocation en premier paramètre. Mais contrairement à call(), apply() prend en second paramètre un tableau représentant la liste des arguments.

Voici un autre exemple plus intéressant que le précédent :

function forevery(list, callback){
    for (var i = 0; i < list.length; i+=1) {
        callback.call(list, i);
    };
}
forevery(['a', 42, false, {}], function(index){
    console.log(this); // [a, 42, false, {}]
    console.log(this[index]); // a, 42, false, {}
    console.log(this[index+1]); // null, a, 42, false
    console.log(this[index-1]); // 42, false, {}, null
});

Dans cet exemple, nous avons déclaré une fonction forevery qui permet d’itérer sur une liste d’éléments. Cette fonction prend en second paramètre une fonction de callback qui sera invoquée pour chaque élément parcouru, avec le contexte de la liste (nous aurions pu spécifier un autre contexte), et nous donne en paramètre l’index de l’élément courant.

Quelle méthode utiliser ?

Cela dépend de votre cas d’usage, et plus précisément cela dépend de comment vous devez gérer vos paramètres. Si vous avez un ensemble de variables que vous devez passer en tant que paramètres, call() serait idéale. Mais si vous avez déjà votre liste de variables sous forme de tableau (par exemple,arguments), apply() est plus appropriée. Prenons par exemple la méthode Math.max(n1, n2, n3, …) de JavaScript : cette méthode calcul le maximum d’une liste de valeur qu’elle prend en paramètre.

Math.max(0,1,2,3,4,5,6,7,8,9);
// 9

Du basique non ? Maintenant, supposons que vous voudriez calculer le maximum (ou le minimum) d’une suite de valeur dont vous ne connaissez pas la taille :

function max(){
    return (arguments.length === 0) ? 0 : Math.max.apply(Math, arguments);
}
max(0,1,2,3,4,5,6,7,8,9); // 9
max(3, 8, 32, 2, 98); // 98
max(42, 0); // 42
max(); // 0

Dans ce bout de code, je viens de déclarer une fonction nommée max  qui calcule le maximum d’une suite de nombre passés en paramètre.  S’il n’y aucun paramètre, le résultat est zéro. Je me repose donc sur le paramètre implicite arguments  pour réaliser cette vérification. Vous comprenez tout de suite qu’utiliser apply() a tout son sens dans cet exemple. Veuillez noter également que j’ai passé Math en tant que contexte d’invocation à Math.max : je n’étais pas obligé d’en spécifier un, mais c’est plus sûr comme ça !

Un mot sur bind()

En JavaScript il existe une autre façon de contrôler le contexte d’invocation d’une fonction. Contrairement à call() et apply(), ce contrôle est fait au moment de la déclaration de la fonction, et non à l’invocation.

En effet, JavaScript propose une méthode bind(contexte) permettant de transformer le contexte d’invocation d’une fonction. Jetons un oeil à l’exemple suivant :

var Foo = function(){
    this.counter = 0;
    this.inc = function(){
        this.counter+=1;
        console.log(this);
        console.log(this.counter);
    };
};

var foo = new Foo();
var element = document.querySelector('#foo');

element.addEventListener('click', foo.inc); // this === element
element.addEventListener('click', function(){ foo.inc(); }); // this === element
element.addEventListener('click', foo.inc.bind(foo)); // this === foo

Nous avons déclaré un constructeur Foo. Ce constructeur possède une méthode inc, qui permet d’incrémenter un compteur counter. Ensuite, nous attachons un événement sur un élément DOM (disons, un bouton par exemple). A chaque clique nous souhaitons incrémenter le compteur deFoo.

Dans un premier temps, nous attachons la méthode foo.inc en tant que listener sur le click. Mais là, problème ! En effet, l’attribut counter n’est pas résolu ; tout simplement puisque le contexte d’invocation de foo.inc n’est pas l’instance foo mais…element (rappelez vous l’exemple cité plus haut avec le apply()).

Pour résoudre ce problème, nous aurions pu passer par une fonction anonyme dans laquelle nous aurions invoqué la méthode foo.inc(). Mais pourquoi déclaré une fonction juste pour invoquer une autre ? Il y a plus simple, nous utilisons la méthode bind() en précisant foo comme contexte d’invocation. Voilà !

Résumé

Dans ce premier épisode, nous avons étudié en détails un des aspects fascinant de JavaScript : JavaScript en tant que langage fonctionnel. J’espère qu’en apprenant comment les fonctions sont traitées en interne par JavaScript, cela vous permettra de changer votre façon d’écrire du code JavaScript, en passant d’un “banale” code qui fonctionne à un code qui fonctionne, oui, mais digne d’un vrai Jedi.

Voilà, nous avons atteint la fin de ce premier épisode ; rendez-vous pour le second épisode dans lequel nous allons tenter de percer le mystère des fermetures en JavaScript.

Que la force soit avec vous…

Standard