[ Web Proxy ]
URL:
Viewing: https://fr.javascript.info/function-object [Back]  [Original]

L'objet Function, NFE
FR

Nous souhaitons rendre ce projet open source disponible pour les gens du monde entier.

Aidez-nous traduire le contenu de ce tutoriel dans votre langue!

    Rechercher sur Javascript.info:
    Rechercher dans le tutoriel:
    Light themeDark theme
    DanskEnglishEspaolFranaisIndonesiaItalianoTrkeOzbek

    Comme nous le savons dj, une fonction en JavaScript est une valeur.

    Chaque valeur en JavaScript a un type. De quel type est une fonction ?

    Pour JavaScript, les fonctions sont des objets.

    Un bon moyen dimaginer des fonctions est en tant que des objets daction quon peut appeler. Nous pouvons non seulement les appeler, mais aussi les traiter comme des objets : ajouter/supprimer des proprits, passer par rfrence, etc.

    La proprit name

    Les objets Fonction contiennent des proprits utilisables.

    Par exemple, le nom dune fonction est accessible en tant que proprit name :

    function sayHi() {
      alert("Hi");
    }
    
    alert(sayHi.name); // sayHi

    Ce qui est drle, cest que la logique dattribution de noms est intelligente. Elle attribue galement le nom correct une fonction mme si elle est cre sans, puis immdiatement attribu :

    let sayHi = function() {
      alert("Hi");
    };
    
    alert(sayHi.name); // sayHi (il y a un nom !)

    Cela fonctionne aussi si laffectation est faite avec une valeur par dfaut :

    function f(sayHi = function() {}) {
      alert(sayHi.name); // sayHi (a marche !)
    }
    
    f();

    Dans la spcification, cette fonctionnalit est appele contextual name. Si la fonction nen fournit pas, elle est dtermine partir du contexte lors de laffectation.

    Les mthodes dobjet ont aussi des noms :

    let user = {
    
      sayHi() {
        // ...
      },
    
      sayBye: function() {
        // ...
      }
    
    }
    
    alert(user.sayHi.name); // sayHi
    alert(user.sayBye.name); // sayBye

    Cependant cest pas magique. Il y a des cas o il ny a aucun moyen de trouver le bon nom. Dans ce cas, la proprit name est vide, comme ci-dessous :

    // fonction cre dans un tableau
    let arr = [function() {}];
    
    alert( arr[0].name ); // <chane de caractres vide>
    // le moteur n'a aucun moyen de dfinir le bon nom. Donc, il n'y en a pas

    Par contre, en pratique la plupart des fonctions ont un nom.

    La proprit length

    Il existe une autre proprit native, length, qui renvoie le nombre de paramtres de la fonction, par exemple :

    function f1(a) {}
    function f2(a, b) {}
    function many(a, b, ...more) {}
    
    alert(f1.length); // 1
    alert(f2.length); // 2
    alert(many.length); // 2

    Nous pouvons voir que les paramtres du reste ne sont pas compts.

    La proprit length est parfois utilise pour la rflxion (introspection en anglais) dans des fonctions qui oprent sur dautres fonctions.

    Par exemple, dans le code ci-dessous, la fonction ask accepte une question poser et un nombre arbitraire de fonctions handler (gestionnaires) appeler.

    Une fois quun utilisateur a fourni sa rponse, la fonction appelle les gestionnaires. Nous pouvons transmettre deux types de gestionnaires :

    • Une fonction sans argument, qui nest appele que lorsque lutilisateur donne une rponse positive.
    • Une fonction avec des arguments, appele dans les deux cas et renvoyant une rponse.

    Pour appeler handler correctement, nous examinons la proprit handler.length.

    Lide est que nous avons une syntaxe de gestionnaire simple, sans argument, pour les cas positifs (variante la plus frquente), mais que nous pouvons galement prendre en charge les gestionnaires universels :

    function ask(question, ...handlers) {
      let isYes = confirm(question);
    
      for(let handler of handlers) {
        if (handler.length == 0) {
          if (isYes) handler();
        } else {
          handler(isYes);
        }
      }
    
    }
    
    // pour une rponse positive, les deux gestionnaires sont appels
    // pour une rponse ngative, seulement le second
    ask("Question?", () => alert('You said yes'), result => alert(result));

    Ceci est un cas particulier de ce quon appelle le polymorphism le traitement des arguments diffremment selon leur type ou, dans notre cas, en fonction de la length. Cette approche est utilise dans les bibliothques JavaScript.

    Proprits personnalises

    Nous pouvons galement ajouter nos propres proprits.

    Nous ajoutons ici la proprit counter pour suivre le nombre total dappels :

    function sayHi() {
      alert("Hi");
    
      // comptons combien de fois nous executons
      sayHi.counter++;
    }
    sayHi.counter = 0; // valeur initiale
    
    sayHi(); // Hi
    sayHi(); // Hi
    
    alert( `Called ${sayHi.counter} times` ); // Appele 2 fois
    Une proprit nest pas une variable

    Une proprit affecte une fonction comme sayHi.counter = 0 ne dfinit pas une variable locale counter lintrieur de celle-ci. En dautres termes, une proprit counter et une variable let counter sont deux choses indpendantes.

    On peut traiter une fonction comme un objet, y stocker des proprits, mais cela na aucun effet sur son excution. Les variables ne sont pas des proprits de fonction et inversement. Ce sont des mondes parallles.

    Les proprits de fonction peuvent parfois remplacer les fermetures. Par exemple, nous pouvons rcrire lexemple de fonction de compteur du chapitre Variable scope, closure pour utiliser une proprit de fonction :

    function makeCounter() {
      // au lieu de :
      // let count = 0
    
      function counter() {
        return counter.count++;
      };
    
      counter.count = 0;
    
      return counter;
    }
    
    let counter = makeCounter();
    alert( counter() ); // 0
    alert( counter() ); // 1

    Le count est maintenant stock dans la fonction directement, pas dans son environnement lexical externe.

    Est-ce meilleur ou pire que dutiliser une fermeture ?

    La principale diffrence est que si la valeur de count rside dans une variable externe, le code externe ne peut pas y accder. Seules les fonctions imbriques peuvent le modifier. Et si cest li une fonction, une telle chose est possible :

    function makeCounter() {
    
      function counter() {
        return counter.count++;
      };
    
      counter.count = 0;
    
      return counter;
    }
    
    let counter = makeCounter();
    
    counter.count = 10;
    alert( counter() ); // 10

    Le choix dpend donc de nos objectifs.

    Fonction Expression Nomme (NFE)

    Fonction Expression Nomme, ou NFE (Named Function Expression en anglais), est un terme pour les fonctions expressions qui ont un nom.

    Par exemple, prenons une fonction expression ordinaire :

    let sayHi = function(who) {
      alert(`Hello, ${who}`);
    };

    Et ajoutons un nom cela :

    let sayHi = function func(who) {
      alert(`Hello, ${who}`);
    };

    Avons-nous ralis quelque chose ici ? Quel est le but de ce nom supplmentaire "func" ?

    Notons dabord que nous avons toujours une expression de fonction. Lajout du nom "func" aprs function nen a pas fait une dclaration de fonction, car il est toujours cr dans le cadre dune expression daffectation.

    Lajout dun tel nom na galement rien cass.

    La fonction est toujours disponible sous la forme sayHi() :

    let sayHi = function func(who) {
      alert(`Hello, ${who}`);
    };
    
    sayHi("John"); // Hello, John

    Il y a deux particularits propos du nom func, voici les raisons :

    1. Il permet la fonction de se rfrencer en interne.
    2. Il nest pas visible en dehors de la fonction.

    Par exemple, la fonction sayHi ci-dessous sappelle nouveau avec "Guest" si aucun who est fourni :

    let sayHi = function func(who) {
      if (who) {
        alert(`Hello, ${who}`);
      } else {
        func("Guest"); // utilise func pour se rappeler
      }
    };
    
    sayHi(); // Hello, Guest
    
    // Mais ceci ne marchera pas :
    func(); // Error, func is not defined (pas visible  l'extrieur de la fonction)

    Pourquoi utilisons-nous func ? Peut-tre juste utiliser sayHi pour lappel imbriqu ?

    En fait, dans la plupart des cas, nous pouvons :

    let sayHi = function(who) {
      if (who) {
        alert(`Hello, ${who}`);
      } else {
        sayHi("Guest");
      }
    };

    Le problme avec ce code est que sayHi peut changer dans le code externe. Si la fonction est assigne une autre variable, le code commencera donner des erreurs :

    let sayHi = function(who) {
      if (who) {
        alert(`Hello, ${who}`);
      } else {
        sayHi("Guest"); // Error: sayHi is not a function
      }
    };
    
    let welcome = sayHi;
    sayHi = null;
    
    welcome(); // Error, l'appel sayHi imbriqu ne fonctionne plus !

    Cela se produit parce que la fonction tire sayHi de son environnement lexical externe. Il ny a pas de sayHi local, donc la variable externe est utilise. Et au moment de lappel, ce sayHi extrieur est null.

    Le nom optionnel que nous pouvons mettre dans lexpression de fonction est destin rsoudre exactement ce type de problmes.

    Utilisons-le pour corriger notre code :

    let sayHi = function func(who) {
      if (who) {
        alert(`Hello, ${who}`);
      } else {
        func("Guest"); // Maintenant tout va bien
      }
    };
    
    let welcome = sayHi;
    sayHi = null;
    
    welcome(); // Hello, Guest (l'appel imbriqu fonctionne)

    Maintenant cela fonctionne, car le nom 'func' est local la fonction. Il nest pas pris de lextrieur (et non visible l-bas). La spcification garantit quelle fera toujours rfrence la fonction actuelle.

    Le code externe a toujours sa variable sayHi ou welcome. Et func est un nom de fonction interne, la faon dont la fonction peut sappeler de manire fiable.

    Il ny a rien de tel pour la dclaration de fonction

    La fonctionnalit nom interne dcrite ici nest disponible que pour les expressions de fonction, pas pour les dclarations de fonction. Pour les dclarations de fonctions, il ny a aucune possibilit de syntaxe dajouter un nom interne supplmentaire.

    Parfois, lorsque nous avons besoin dun nom interne fiable, cest la raison pour laquelle nous rcrivons une dclaration de fonction en tant quexpression de fonction nomme.

    Rsum

    Les fonctions sont des objets.

    Ici nous avons couvert leurs proprits :

    • name le nom de la fonction. Habituellement tir de la dfinition de la fonction, mais sil nen existe pas, JavaScript essaie de le deviner partir du contexte (par exemple, une affectation).
    • length le nombre darguments dans la dfinition de la fonction. Les paramtres du reste ne sont pas compts.

    Si la fonction est dclare en tant quexpression de fonction (et non dans le flux du code principal) et quelle porte un nom, elle est appele expression de fonction nomme. Le nom peut tre utilis lintrieur pour se rfrencer, pour des appels rcursifs ou autres.

    Les fonctions peuvent galement comporter des proprits supplmentaires. De nombreuses bibliothques JavaScript bien connues font un grand usage de cette fonctionnalit.

    Elles crent une fonction principale et y attachent de nombreuses autres fonctions dassistance. Par exemple, la bibliothque jQuery cre une fonction nomme $. La bibliothque lodash cre une fonction _ et ajoute ensuite _.clone, _.keyBy et dautres proprits (voir la doc lorsque vous souhaitez en savoir plus leur sujet). En fait, elles le font pour rduire leur pollution de lespace global, de sorte quune seule bibliothque ne donne quune seule variable globale. Cela rduit la possibilit de conflits de noms.

    Ainsi, une fonction peut faire un travail utile par elle-mme et aussi porter un tas dautres fonctionnalits dans les proprits.

    Exercices

    importance: 5

    Modifiez le code de makeCounter() afin que le compteur puisse galement diminuer et dfinir le nombre:

    • counter() devrait retourner le nombre suivant (comme avant).
    • counter.set(value) devrait dfinir le compteur value.
    • counter.decrease() devrait dcrmenter le compteur de 1.

    Voir le code sandbox pour un exemple dutilisation complet.

    P.S. Vous pouvez utiliser une fermeture ou la proprit de fonction pour maintenir le nombre actuel. Ou crivez les deux variantes.

    Open a sandbox with tests.

    solution

    La solution utilise count dans la variable locale, mais les mthodes daddition sont crites directement dans le compteur. Ils partagent le mme environnement lexical extrieur et peuvent galement accder au count actuel.

    function makeCounter() {
      let count = 0;
    
      function counter() {
        return count++;
      }
    
      counter.set = value => count = value;
    
      counter.decrease = () => count--;
    
      return counter;
    }

    Ouvrez la solution avec des tests dans une sandbox.

    importance: 2

    crivez la fonction sum qui fonctionnerait comme ceci:

    sum(1)(2) == 3; // 1 + 2
    sum(1)(2)(3) == 6; // 1 + 2 + 3
    sum(5)(-1)(2) == 6
    sum(6)(-1)(-2)(-3) == 0
    sum(0)(1)(2)(3)(4)(5) == 15

    P.S. Indice: vous devrez peut-tre configurer une conversion dobjet primitive personnalis pour votre fonction.

    Open a sandbox with tests.

    solution
    1. Pour que tout fonctionne * de toute faon *, le rsultat de sum doit tre fonction.
    2. Cette fonction doit garder en mmoire la valeur actuelle entre les appels.
    3. Selon la tche, la fonction doit devenir le numro lorsquelle est utilise dans ==. Les fonctions tant des objets, la conversion seffectue comme dcrit dans le chapitre Conversion d'objet en primitive, et nous pouvons fournir notre propre mthode qui renvoie le nombre.

    Maintenant le code:

    function sum(a) {
    
      let currentSum = a;
    
      function f(b) {
        currentSum += b;
        return f;
      }
    
      f.toString = function() {
        return currentSum;
      };
    
      return f;
    }
    
    alert( sum(1)(2) ); // 3
    alert( sum(5)(-1)(2) ); // 6
    alert( sum(6)(-1)(-2)(-3) ); // 0
    alert( sum(0)(1)(2)(3)(4)(5) ); // 15

    Veuillez noter que la fonction sum ne fonctionne rellement quune fois. Il renvoie la fonction f.

    Ensuite, chaque appel suivant, f ajoute son paramtre la somme currentSum, et se renvoie lui-mme.

    Il ny a pas de rcursion dans la dernire ligne de f.

    Voici quoi ressemble la rcursion:

    function f(b) {
      currentSum += b;
      return f(); // <-- appel rcursif
    }

    Et dans notre cas, nous renvoyons simplement la fonction, sans lappeler:

    function f(b) {
      currentSum += b;
      return f; // <-- ne s'appelle pas, se renvoie
    }

    Ce f sera utilis lors du prochain appel et se renvera lui-mme autant de fois que ncessaire. Ensuite, lorsquil est utilis sous forme de nombre ou de chane de caractres, le toString renvoie le currentSum. Nous pourrions aussi utiliser Symbol.toPrimitive ou valueOf ici pour la conversion.

    Ouvrez la solution avec des tests dans une sandbox.

    Carte du tutoriel

    Commentaires

    lire ceci avant de commenter
    • Si vous avez des amliorations suggrer, merci de soumettre une issue GitHub ou une pull request au lieu de commenter.
    • Si vous ne comprenez pas quelque chose dans l'article, merci de prciser.
    • Pour insrer quelques bouts de code, utilisez la balise <code>, pour plusieurs lignes enveloppez-les avec la balise <pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen)

    Web Proxy Viewer  |  New URL  |  Original Page