Recherche…


Introduction

Fonctions en JavaScript fournissent un code organisé et réutilisable pour effectuer un ensemble d'actions. Les fonctions simplifient le processus de codage, empêchent la logique redondante et facilitent le suivi du code. Cette rubrique décrit la déclaration et l'utilisation des fonctions, des arguments, des paramètres, des instructions de retour et de la portée en JavaScript.

Syntaxe

  • exemple de fonction (x) {return x}

  • var exemple = function (x) {return x}

  • (fonction() { ... })(); // Expression de la fonction immédiatement invoquée (IIFE)

  • var instance = new Exemple (x);

  • Les méthodes

  • fn.apply (valueForThis [, arrayOfArgs])

  • fn.bind (valueForThis [, arg1 [, arg2, ...]])

  • fn.call (valueForThis [, arg1 [, arg2, ...]])

  • ES2015 + (ES6 +):

  • const exemple = x => {return x}; // retour explicite de la fonction flèche

  • const exemple = x => x; // retour implicite de la fonction flèche

  • const exemple = (x, y, z) => {...} // Fonction flèche plusieurs arguments

  • (() => {...}) (); // IIFE en utilisant une fonction de flèche

Remarques

Pour plus d'informations sur les fonctions fléchées, consultez la documentation sur les fonctions de flèche .

Fonctionne comme une variable

Une déclaration de fonction normale ressemble à ceci:

function foo(){
}

Une fonction définie comme celle-ci est accessible de n'importe où dans son contexte par son nom. Mais parfois, il peut être utile de traiter des références de fonctions comme des références d'objet. Par exemple, vous pouvez affecter un objet à une variable en fonction d'un ensemble de conditions, puis récupérer ultérieurement une propriété de l'un ou l'autre objet:

var name = 'Cameron';
var spouse;

if ( name === 'Taylor' ) spouse = { name: 'Jordan' };
else if ( name === 'Cameron' ) spouse = { name: 'Casey' };

var spouseName = spouse.name;

En JavaScript, vous pouvez faire la même chose avec les fonctions:

// Example 1
var hashAlgorithm = 'sha1';
var hash;

if ( hashAlgorithm === 'sha1' ) hash = function(value){ /*...*/ };
else if ( hashAlgorithm === 'md5' ) hash = function(value){ /*...*/ };

hash('Fred');

Dans l'exemple ci-dessus, le hash est une variable normale. On lui attribue une référence à une fonction, après quoi la fonction à laquelle il fait référence peut être appelée à l'aide de parenthèses, tout comme une déclaration de fonction normale.

L'exemple ci-dessus fait référence à des fonctions anonymes ... fonctions qui n'ont pas de nom propre. Vous pouvez également utiliser des variables pour faire référence à des fonctions nommées. L'exemple ci-dessus pourrait être réécrit comme ceci:

// Example 2
var hashAlgorithm = 'sha1';
var hash;

if ( hashAlgorithm === 'sha1' ) hash = sha1Hash;
else if ( hashAlgorithm === 'md5' ) hash = md5Hash;

hash('Fred');

function md5Hash(value){
    // ...
}

function sha1Hash(value){
    // ...
}

Vous pouvez également affecter des références de fonction à partir des propriétés de l'objet:

// Example 3
var hashAlgorithms = {
    sha1: function(value) { /**/ },
    md5: function(value) { /**/ }
};

var hashAlgorithm = 'sha1';
var hash;

if ( hashAlgorithm === 'sha1' ) hash = hashAlgorithms.sha1;
else if ( hashAlgorithm === 'md5' ) hash = hashAlgorithms.md5;

hash('Fred');

Vous pouvez affecter la référence à une fonction détenue par une variable à une autre en omettant les parenthèses. Cela peut entraîner une erreur facile à réaliser: tenter d'attribuer la valeur de retour d'une fonction à une autre variable, mais affecter accidentellement la référence à la fonction.

// Example 4
var a = getValue;
var b = a; // b is now a reference to getValue.
var c = b(); // b is invoked, so c now holds the value returned by getValue (41)

function getValue(){
    return 41;
}

Une référence à une fonction est comme toute autre valeur. Comme vous l'avez vu, une référence peut être attribuée à une variable et la valeur de référence de cette variable peut être affectée ultérieurement à d'autres variables. Vous pouvez transmettre des références à des fonctions comme toute autre valeur, y compris transmettre une référence à une fonction en tant que valeur de retour d'une autre fonction. Par exemple:

// Example 5
// getHashingFunction returns a function, which is assigned
// to hash for later use:
var hash = getHashingFunction( 'sha1' );
// ...
hash('Fred');


// return the function corresponding to the given algorithmName
function getHashingFunction( algorithmName ){
    // return a reference to an anonymous function
    if (algorithmName === 'sha1') return function(value){ /**/ };
    // return a reference to a declared function
    else if (algorithmName === 'md5') return md5;
}

function md5Hash(value){
    // ...
}

Vous n'avez pas besoin d'affecter une référence de fonction à une variable pour l'invoquer. Cet exemple, basé sur l'exemple 5, appelle getHashingFunction, puis appelle immédiatement la fonction renvoyée et transmet sa valeur de retour à hashedValue.

// Example 6
var hashedValue = getHashingFunction( 'sha1' )( 'Fred' );

Une note sur le levage

Gardez à l'esprit que, contrairement aux déclarations de fonctions normales, les variables qui référencent des fonctions ne sont pas "hissées". Dans l'exemple 2, les fonctions md5Hash et sha1Hash sont définies au bas du script, mais sont disponibles partout immédiatement. Où que vous définissiez une fonction, l'interpréteur le "haut" de sa portée, le rendant immédiatement disponible. Ce n'est pas le cas pour les définitions de variables, donc le code comme celui-ci se brise:

var functionVariable;

hoistedFunction(); // works, because the function is "hoisted" to the top of its scope
functionVariable(); // error: undefined is not a function.

function hoistedFunction(){}
functionVariable = function(){};

Fonction anonyme

Définition d'une fonction anonyme

Lorsqu'une fonction est définie, vous lui attribuez souvent un nom, puis l'invoque en utilisant ce nom, comme ceci:

foo();

function foo(){
    // ...
}

Lorsque vous définissez une fonction de cette façon, le moteur d'exécution Javascript stocke votre fonction en mémoire, puis crée une référence à cette fonction, en utilisant le nom que vous lui avez attribué. Ce nom est alors accessible dans le périmètre actuel. Cela peut être un moyen très pratique de créer une fonction, mais Javascript ne vous oblige pas à attribuer un nom à une fonction. Ce qui suit est également parfaitement légal:

function() {
    // ...
}

Lorsqu'une fonction est définie sans nom, il s'agit d'une fonction anonyme. La fonction est stockée en mémoire, mais le runtime ne crée pas automatiquement une référence pour vous. À première vue, cela peut sembler inutile, mais il existe plusieurs scénarios où les fonctions anonymes sont très pratiques.

Affectation d'une fonction anonyme à une variable

Un usage très courant des fonctions anonymes est de les affecter à une variable:

var foo = function(){ /*...*/ };

foo();

Cette utilisation des fonctions anonymes est décrite plus en détail dans Fonctions en tant que variable

Fourniture d'une fonction anonyme en tant que paramètre à une autre fonction

Certaines fonctions peuvent accepter une référence à une fonction en tant que paramètre. Ils sont parfois appelés "injections de dépendances" ou "rappels", car ils permettent à la fonction d'appeler votre code, ce qui vous permet de modifier le comportement de la fonction appelée. Par exemple, la fonction map de l'objet Array vous permet de parcourir chaque élément d'un tableau, puis de créer un nouveau tableau en appliquant une fonction de transformation à chaque élément.

var nums = [0,1,2];
var doubledNums = nums.map( function(element){ return element * 2; } ); // [0,2,4]

Il serait fastidieux, bâclé et inutile de créer une fonction nommée, qui encombrerait votre portée avec une fonction nécessaire à cet endroit et briserait le flux naturel et la lecture de votre code (un collègue devrait laisser ce code pour trouver votre fonction pour comprendre ce qui se passe).

Renvoi d'une fonction anonyme à partir d'une autre fonction

Parfois, il est utile de retourner une fonction à la suite d'une autre fonction. Par exemple:

var hash = getHashFunction( 'sha1' );
var hashValue = hash( 'Secret Value' );

function getHashFunction( algorithm ){

    if ( algorithm === 'sha1' ) return function( value ){ /*...*/ };
    else if ( algorithm === 'md5' ) return function( value ){ /*...*/ };

}

Invoquer immédiatement une fonction anonyme

Contrairement à beaucoup d'autres langages, la portée en Javascript est au niveau de la fonction, pas au niveau du bloc. (Voir la portée de la fonction ). Dans certains cas, cependant, il est nécessaire de créer une nouvelle portée. Par exemple, il est courant de créer une nouvelle portée lors de l'ajout de code via une <script> , plutôt que de permettre la définition de noms de variables dans la portée globale (ce qui risque d'entraîner la collision d'autres scripts avec vos noms de variables). Une méthode courante pour gérer cette situation consiste à définir une nouvelle fonction anonyme et à l'invoquer immédiatement, en masquant en toute sécurité vos variables dans le cadre de la fonction anonyme et sans rendre votre code accessible à des tiers via un nom de fonction divulgué. Par exemple:

<!-- My Script -->
<script>
function initialize(){
    // foo is safely hidden within initialize, but...
    var foo = '';
}

// ...my initialize function is now accessible from global scope.
// There's a risk someone could call it again, probably by accident.
initialize();
</script>

<script>
// Using an anonymous function, and then immediately
// invoking it, hides my foo variable and guarantees
// no one else can call it a second time.
(function(){
    var foo = '';
}()) // <--- the parentheses invokes the function immediately
</script>

Fonctions anonymes auto-référentielles

Parfois, il est utile qu'une fonction anonyme puisse se référer à elle-même. Par exemple, la fonction peut avoir besoin de s'appeler récursivement ou d'ajouter des propriétés à elle-même. Si la fonction est anonyme, cela peut être très difficile car elle nécessite la connaissance de la variable à laquelle la fonction a été affectée. C'est la solution moins qu'idéale:

var foo = function(callAgain){
    console.log( 'Whassup?' );
    // Less then ideal... we're dependent on a variable reference...
    if (callAgain === true) foo(false);
};

foo(true);

// Console Output:
// Whassup?
// Whassup?

// Assign bar to the original function, and assign foo to another function.
var bar = foo;
foo = function(){
    console.log('Bad.')
};

bar(true);

// Console Output:
// Whassup?
// Bad.

L'intention ici était que la fonction anonyme appelle elle-même récursivement, mais lorsque la valeur de foo change, vous vous retrouvez avec un bogue potentiellement difficile à suivre.

Au lieu de cela, nous pouvons donner à la fonction anonyme une référence à elle-même en lui donnant un nom privé, comme ceci:

var foo = function myself(callAgain){
    console.log( 'Whassup?' );
    // Less then ideal... we're dependent on a variable reference...
    if (callAgain === true) myself(false);
};

foo(true);

// Console Output:
// Whassup?
// Whassup?

// Assign bar to the original function, and assign foo to another function.
var bar = foo;
foo = function(){
    console.log('Bad.')
};

bar(true);

// Console Output:
// Whassup?
// Whassup?

Notez que le nom de la fonction est défini sur lui-même. Le nom n'a pas filtré dans la portée externe:

myself(false); // ReferenceError: myself is not defined

Cette technique est particulièrement utile lorsque vous utilisez des fonctions anonymes récursives comme paramètres de rappel:

5
// Calculate the fibonacci value for each number in an array:
var fib = false,
    result = [1,2,3,4,5,6,7,8].map(
        function fib(n){
            return ( n <= 2 ) ? 1 : fib( n - 1 ) + fib( n - 2 );
        });
// result = [1, 1, 2, 3, 5, 8, 13, 21]
// fib = false (the anonymous function name did not overwrite our fib variable)

Expressions de fonction immédiatement invoquées

Parfois, vous ne voulez pas que votre fonction soit accessible / stockée en tant que variable. Vous pouvez créer une expression de fonction appelée immédiatement (IIFE en abrégé). Ce sont essentiellement des fonctions anonymes auto-exécutables . Ils ont accès à la portée environnante, mais la fonction elle-même et toutes les variables internes seront inaccessibles de l'extérieur. Une chose importante à noter à propos d'IIFE est que même si vous nommez votre fonction, les fonctions standard ne sont pas hissées comme les fonctions standard sont et ne peuvent pas être appelées par le nom de la fonction avec laquelle elles sont déclarées.

(function() {
   alert("I've run - but can't be run again because I'm immediately invoked at runtime,
          leaving behind only the result I generate");
}());

Ceci est une autre façon d'écrire l'IIFE. Notez que la parenthèse fermante avant le point-virgule a été déplacée et placée juste après le crochet:

(function() {
   alert("This is IIFE too.");
})();

Vous pouvez facilement transmettre des paramètres dans un IIFE:

(function(message) {
   alert(message);
}("Hello World!"));

En outre, vous pouvez renvoyer des valeurs à la portée environnante:

var example = (function() {
   return 42;
}());
console.log(example); // => 42

Si nécessaire, il est possible de nommer un IIFE. Bien que moins souvent vu, ce modèle présente plusieurs avantages, comme fournir une référence qui peut être utilisée pour une récursivité et rendre le débogage plus simple, car le nom est inclus dans la pile d'appels.

(function namedIIFE() { 
    throw error; // We can now see the error thrown in 'namedIIFE()'
}());

Bien que l’encapsulation d’une fonction entre parenthèses soit la manière la plus courante de désigner le parseur Javascript comme s’attendant à une expression, dans les endroits où une expression est déjà attendue, la notation peut être plus concise:

var a = function() { return 42 }();
console.log(a)  // => 42

Version flèche de la fonction immédiatement invoquée:

6
(() => console.log("Hello!"))(); // => Hello!

Détermination de la fonction

Lorsque vous définissez une fonction, elle crée une portée .

Tout ce qui est défini dans la fonction n'est pas accessible par code en dehors de la fonction. Seul le code de cette étendue peut voir les entités définies dans la portée.

function foo() {
  var a = 'hello';
  console.log(a);  // => 'hello'
}

console.log(a);  // reference error

Les fonctions imbriquées sont possibles en JavaScript et les mêmes règles s'appliquent.

function foo() {
  var a = 'hello';
  
  function bar() {
    var b = 'world';
    console.log(a);  // => 'hello'
    console.log(b);  // => 'world'
  }

  console.log(a);  // => 'hello'
  console.log(b);  // reference error
}

console.log(a);  // reference error
console.log(b);  // reference error

Lorsque JavaScript tente de résoudre une référence ou une variable, il commence à le rechercher dans la portée actuelle. Si elle ne parvient pas à trouver cette déclaration dans le champ d’application actuel, elle l’accède à un objectif. Ce processus se répète jusqu'à ce que la déclaration ait été trouvée. Si l'analyseur JavaScript atteint la portée globale et ne parvient toujours pas à trouver la référence, une erreur de référence sera générée.

var a = 'hello';

function foo() {
  var b = 'world';

  function bar() {
    var c = '!!';

    console.log(a);  // => 'hello'
    console.log(b);  // => 'world'
    console.log(c);  // => '!!'
    console.log(d);  // reference error
  }
}

Ce comportement d'escalade peut également signifier qu'une référence peut "ombrer" une référence nommée de la même manière dans la portée externe depuis sa première apparition.

var a = 'hello';

function foo() {
  var a = 'world';

  function bar() {
    console.log(a);  // => 'world'
  }
}
6

La façon dont JavaScript résout la portée s'applique également au mot clé const . Déclarer une variable avec le mot-clé const implique que vous n'êtes pas autorisé à réaffecter la valeur, mais la déclarer dans une fonction créera une nouvelle étendue et avec cela une nouvelle variable.

function foo() {
  const a = true;

  function bar() {
    const a = false;  // different variable
    console.log(a);   // false
  }

  const a = false;    // SyntaxError
  a = false;          // TypeError
  console.log(a);     // true
}

Cependant, les fonctions ne sont pas les seuls blocs qui créent une étendue (si vous utilisez let ou const ). let déclarations let et const ont une portée de l'instruction de bloc la plus proche. Voir ici pour une description plus détaillée.

Liaison `this` et arguments

5.1

Lorsque vous faites référence à une méthode (une propriété qui est une fonction) dans JavaScript, elle ne se souvient généralement pas de l'objet auquel elle était initialement attachée. Si la méthode a besoin de se référer à cet objet que this ne sera pas en mesure, et l' appelant sera probablement causer un accident.

Vous pouvez utiliser la méthode .bind() sur une fonction pour créer un wrapper qui inclut la valeur de this et un nombre quelconque d'arguments principaux.

var monitor = {
  threshold: 5,
  check: function(value) {
    if (value > this.threshold) {
      this.display("Value is too high!");
    }
  },
  display(message) {
    alert(message);
  }
};

monitor.check(7); // The value of `this` is implied by the method call syntax.


var badCheck = monitor.check;
badCheck(15); // The value of `this` is window object and this.threshold is undefined, so value > this.threshold is false

var check = monitor.check.bind(monitor);
check(15); // This value of `this` was explicitly bound, the function works.

var check8 = monitor.check.bind(monitor, 8);
check8(); // We also bound the argument to `8` here. It can't be re-specified.

Lorsqu'ils ne sont pas en mode strict, une fonction utilise l'objet global ( window dans le navigateur) comme this , à moins que la fonction est appelée comme méthode, liée, ou appelée avec la méthode .call syntaxe.

window.x = 12; 

function example() {
  return this.x;
}

console.log(example()); // 12

En mode strict, this n'est undefined par défaut

window.x = 12; 
    
function example() {
  "use strict";
  return this.x;
}

console.log(example()); // Uncaught TypeError: Cannot read property 'x' of undefined(…)
7

Opérateur de liaison

L' opérateur de liaison à deux points doubles peut être utilisé comme syntaxe abrégée pour le concept expliqué ci-dessus:

var log = console.log.bind(console); // long version
const log = ::console.log; // short version

foo.bar.call(foo); // long version
foo::bar(); // short version

foo.bar.call(foo, arg1, arg2, arg3); // long version
foo::bar(arg1, arg2, arg3); // short version

foo.bar.apply(foo, args); // long version
foo::bar(...args); // short version

Cette syntaxe vous permet d'écrire normalement, sans se soucier de lier this partout.

Liaison des fonctions de la console aux variables

var log = console.log.bind(console);

Usage:

log('one', '2', 3, [4], {5: 5});

Sortie:

one 2 3 [4] Object {5: 5}

Pourquoi ferais-tu ça?

Un cas d'utilisation peut être lorsque vous avez un enregistreur personnalisé et que vous souhaitez décider quel environnement utiliser.

var logger = require('appLogger');

var log = logToServer ? logger.log : console.log.bind(console);

Fonction Arguments, objet "arguments", paramètres de repos et de propagation

Les fonctions peuvent prendre des entrées sous la forme de variables pouvant être utilisées et assignées dans leur propre domaine. La fonction suivante prend deux valeurs numériques et renvoie leur somme:

function addition (argument1, argument2){
    return argument1 + argument2; 
}

console.log(addition(2, 3)); // -> 5

objet arguments

L'objet arguments contient tous les paramètres de la fonction qui contiennent une valeur autre que celle par défaut . Il peut également être utilisé même si les paramètres ne sont pas explicitement déclarés:

(function() { console.log(arguments) })(0,'str', [2,{3}]) // -> [0, "str", Array[2]]

Bien que lors de l'impression d' arguments la sortie ressemble à un tableau, c'est en fait un objet:

(function() { console.log(typeof arguments) })(); // -> object

Paramètres de repos: function (...parm) {}

Dans ES6, la ... syntaxe lorsqu'elle est utilisée dans la déclaration des paramètres d'une fonction transforme la variable à son droit en un seul objet contenant tous les autres paramètres fournis après ceux déclarés. Cela permet à la fonction d'être invoquée avec un nombre illimité d'arguments, qui feront partie de cette variable:

(function(a, ...b){console.log(typeof b+': '+b[0]+b[1]+b[2]) })(0,1,'2',[3],{i:4});
// -> object: 123  

Paramètres de propagation: function_name(...varb);

Dans ES6, la syntaxe ... peut également être utilisée lors de l'appel d'une fonction en plaçant un objet / variable à sa droite. Cela permet aux éléments de cet objet d'être passés dans cette fonction en un seul objet:

let nums = [2,42,-1];
console.log(...['a','b','c'], Math.max(...nums)); // -> a b c 42

Fonctions nommées

Les fonctions peuvent être nommées ou non nommées ( fonctions anonymes ):

var namedSum = function sum (a, b) { // named
    return a + b;
}

var anonSum = function (a, b) { // anonymous
    return a + b;
}

namedSum(1, 3);
anonSum(1, 3);

4
4

Mais leurs noms sont privés à leur propre portée:

var sumTwoNumbers = function sum (a, b) {
    return a + b;
}

sum(1, 3);

Uncaught ReferenceError: la somme n'est pas définie

Les fonctions nommées diffèrent des fonctions anonymes dans plusieurs scénarios:

  • Lorsque vous déboguez, le nom de la fonction apparaîtra dans la trace d'erreur / pile
  • Les fonctions nommées sont hissées alors que les fonctions anonymes ne sont pas
  • Les fonctions nommées et les fonctions anonymes se comportent différemment lors de la gestion de la récursivité
  • Selon la version ECMAScript, les fonctions nommées et anonymes peuvent traiter différemment la propriété du name fonction

Les fonctions nommées sont hissées

Lors de l'utilisation d'une fonction anonyme, la fonction ne peut être appelée qu'après la ligne de déclaration, alors qu'une fonction nommée peut être appelée avant la déclaration. Considérer

foo();
var foo = function () { // using an anonymous function
    console.log('bar');
}

UnCaught TypeError: foo n'est pas une fonction

foo();
function foo () { // using a named function
    console.log('bar');
}

bar


Fonctions nommées dans un scénario récursif

Une fonction récursive peut être définie comme:

var say = function (times) {
    if (times > 0) {
        console.log('Hello!');

        say(times - 1);
    }
}

//you could call 'say' directly, 
//but this way just illustrates the example
var sayHelloTimes = say;

sayHelloTimes(2);

salut!
salut!

Que se passe-t-il si quelque part dans votre code la liaison de la fonction d'origine est redéfinie?

var say = function (times) {
    if (times > 0) {
        console.log('Hello!');

        say(times - 1);
    }
}

var sayHelloTimes = say;
say = "oops";

sayHelloTimes(2);

salut!
UnCaught TypeError: say n'est pas une fonction

Cela peut être résolu en utilisant une fonction nommée

// The outer variable can even have the same name as the function
// as they are contained in different scopes
var say = function say (times) {
    if (times > 0) {
        console.log('Hello!');

        // this time, 'say' doesn't use the outer variable
        // it uses the named function
        say(times - 1);
    }
}

var sayHelloTimes = say;
say = "oops";

sayHelloTimes(2);

salut!
salut!

Et en prime, la fonction nommée ne peut pas être définie sur undefined , même depuis l'intérieur:

var say = function say (times) {
    // this does nothing
    say = undefined;
    
    if (times > 0) {
        console.log('Hello!');

        // this time, 'say' doesn't use the outer variable
        // it's using the named function
        say(times - 1);
    }
}

var sayHelloTimes = say;
say = "oops";

sayHelloTimes(2);

salut!
salut!


La propriété name des fonctions

Avant ES6, les fonctions nommées avaient leurs name propriétés définies à leurs noms de fonctions, et les fonctions anonymes avaient leur name propriétés sont définies sur la chaîne vide.

5
var foo = function () {}
console.log(foo.name); // outputs ''

function foo () {}
console.log(foo.name); // outputs 'foo'

Post ES6, les fonctions nommées et non nommées définissent toutes les deux leurs propriétés de name :

6
var foo = function () {}
console.log(foo.name); // outputs 'foo'

function foo () {}
console.log(foo.name); // outputs 'foo'

var foo = function bar () {}
console.log(foo.name); // outputs 'bar'

Fonction récursive

Une fonction récursive est simplement une fonction, qui s'appellerait elle-même.

function factorial (n) {
    if (n <= 1) {
        return 1;
    }
    
    return n * factorial(n - 1);
}

La fonction ci-dessus montre un exemple de base sur la façon d'effectuer une fonction récursive pour renvoyer une factorielle.


Un autre exemple serait de récupérer la somme des nombres pairs dans un tableau.

function countEvenNumbers (arr) {
    // Sentinel value.  Recursion stops on empty array.
    if (arr.length < 1) {
        return 0;
    }
    // The shift() method removes the first element from an array 
    // and returns that element. This method changes the length of the array.
    var value = arr.shift();

    // `value % 2 === 0` tests if the number is even or odd
    // If it's even we add one to the result of counting the remainder of 
    // the array.  If it's odd, we add zero to it.
    return ((value % 2 === 0) ? 1 : 0) + countEvens(arr);
}

Il est important que ces fonctions effectuent une sorte de vérification de la valeur sentinelle pour éviter les boucles infinies. Dans le premier exemple ci-dessus, lorsque n est inférieur ou égal à 1, la récursivité s'arrête, permettant au résultat de chaque appel de renvoyer la pile d'appels.

Currying

Le curry est la transformation d'une fonction de n arité ou d'arguments en une suite de n fonctions ne prenant qu'un seul argument.

Cas d'utilisation: Lorsque les valeurs de certains arguments sont disponibles avant les autres, vous pouvez utiliser le currying pour décomposer une fonction en une série de fonctions qui complètent le travail par étapes, à mesure que chaque valeur arrive. Cela peut être utile:

  • Lorsque la valeur d'un argument ne change presque jamais (par exemple, un facteur de conversion), vous devez conserver la flexibilité de définir cette valeur (plutôt que de la coder en tant que constante).
  • Lorsque le résultat d'une fonction curry est utile avant que les autres fonctions curry aient été exécutées.
  • Valider l'arrivée des fonctions dans une séquence spécifique.

Par exemple, le volume d'un prisme rectangulaire peut s'expliquer par une fonction de trois facteurs: longueur ( l ), largeur ( w ) et hauteur ( h ):

var prism = function(l, w, h) {
    return l * w * h;
}

Une version curry de cette fonction ressemblerait à ceci:

function prism(l) {
    return function(w) {
        return function(h) {
            return l * w * h;
        }
    }
}
6
// alternatively, with concise ECMAScript 6+ syntax:
var prism = l => w => h => l * w * h;

Vous pouvez appeler ces séquences de fonctions avec le prism(2)(3)(5) , qui doit être évalué à 30.

En l'absence de machines supplémentaires (comme avec les bibliothèques), le curry a une flexibilité syntaxique limitée en JavaScript (ES 5/6) en raison de l'absence de valeurs de substitution; Ainsi, alors que vous pouvez utiliser var a = prism(2)(3) pour créer une fonction partiellement appliquée , vous ne pouvez pas utiliser le prism()(3)(5) .

Utilisation de la déclaration de retour

L'instruction return peut être un moyen utile de créer une sortie pour une fonction. L'instruction return est particulièrement utile si vous ne savez pas dans quel contexte la fonction sera utilisée.

//An example function that will take a string as input and return 
//the first character of the string.

function firstChar (stringIn){
    return stringIn.charAt(0);
}

Maintenant, pour utiliser cette fonction, vous devez la placer à la place d'une variable ailleurs dans votre code:

Utiliser la fonction result comme argument pour une autre fonction:

console.log(firstChar("Hello world"));

La sortie de la console sera:

> H

La déclaration de retour termine la fonction

Si nous modifions la fonction au début, nous pouvons démontrer que l'instruction return termine la fonction.

function firstChar (stringIn){
    console.log("The first action of the first char function");
    return stringIn.charAt(0);
    console.log("The last action of the first char function");
}

Exécuter cette fonction comme ça ressemblera à ceci:

console.log(firstChar("JS"));

Sortie de la console:

> The first action of the first char function
> J

Il n'imprimera pas le message après la déclaration de retour, car la fonction est maintenant terminée.

Déclaration renvoyant sur plusieurs lignes:

En JavaScript, vous pouvez normalement diviser une ligne de code en plusieurs lignes à des fins de lisibilité ou d'organisation. Ceci est valide JavaScript:

var
    name = "bob",
    age = 18;

Lorsque JavaScript voit une instruction incomplète comme var il regarde la ligne suivante pour se compléter. Cependant, si vous faites la même erreur avec la déclaration de return , vous n'obtiendrez pas ce que vous attendiez.

return
    "Hi, my name is "+ name + ". " +
    "I'm "+ age + " years old.";

Ce code retournera undefined car return en lui-même est une instruction complète en Javascript, il ne se tournera donc pas vers la ligne suivante pour se compléter. Si vous devez diviser une déclaration de return en plusieurs lignes, mettez une valeur à renvoyer avant de la diviser, comme cela.

return "Hi, my name is " + name + ". " +
    "I'm " + age + " years old.";

Passer des arguments par référence ou valeur

En JavaScript, tous les arguments sont passés par valeur. Lorsqu'une fonction assigne une nouvelle valeur à une variable d'argument, cette modification ne sera pas visible pour l'appelant:

var obj = {a: 2};
function myfunc(arg){
    arg = {a: 5}; // Note the assignment is to the parameter variable itself
}
myfunc(obj);
console.log(obj.a); // 2

Cependant, les modifications apportées aux propriétés (imbriquées) de tels arguments seront visibles par l'appelant:

var obj = {a: 2};
function myfunc(arg){
    arg.a = 5; // assignment to a property of the argument
}
myfunc(obj);
console.log(obj.a); // 5

Cela peut être considéré comme un appel par référence: même si une fonction ne peut pas changer l'objet de l'appelant en attribuant une nouvelle valeur à lui, il pourrait muter l'objet de l'appelant.

Comme les arguments de valeur primitive, tels que les nombres ou les chaînes de caractères, sont immuables, il est impossible pour une fonction de les muter:

var s = 'say';
function myfunc(arg){
    arg += ' hello'; // assignment to the parameter variable itself
}
myfunc(s);
console.log(s); // 'say'

Lorsqu'une fonction veut modifier un objet passé en argument, mais ne veut pas réellement modifier l'objet de l'appelant, la variable d'argument doit être réaffectée:

6
var obj = {a: 2, b: 3};
function myfunc(arg){
    arg = Object.assign({}, arg); // assignment to argument variable, shallow copy
    arg.a = 5;
}
myfunc(obj);
console.log(obj.a); // 2

En guise d'alternative à la mutation sur place d'un argument, les fonctions peuvent créer une nouvelle valeur, basée sur l'argument, et la renvoyer. L'appelant peut alors l'affecter, même à la variable d'origine transmise en argument:

var a = 2;
function myfunc(arg){
    arg++;
    return arg;
}
a = myfunc(a);
console.log(obj.a); // 3

Appeler et appliquer

Les fonctions ont deux méthodes intégrées qui permettent au programmeur de fournir des arguments et la this différemment variable: call et apply .

Ceci est utile car les fonctions qui fonctionnent sur un objet (l’objet dont elles sont la propriété) peuvent être réutilisées pour fonctionner sur un autre objet compatible. De plus, des arguments peuvent être donnés en une seule fois sous forme de tableaux, de la même manière que l'opérateur de propagation ( ... ) dans ES6.

let obj = {
    a: 1,
    b: 2,
    set: function (a, b) {
        this.a = a;
        this.b = b;
    }
};

obj.set(3, 7); // normal syntax
obj.set.call(obj, 3, 7); // equivalent to the above
obj.set.apply(obj, [3, 7]); // equivalent to the above; note that an array is used

console.log(obj); // prints { a: 3, b: 5 }

let myObj = {};
myObj.set(5, 4); // fails; myObj has no `set` property
obj.set.call(myObj, 5, 4); // success; `this` in set() is re-routed to myObj instead of obj
obj.set.apply(myObj, [5, 4]); // same as above; note the array

console.log(myObj); // prints { a: 3, b: 5 }
5

ECMAScript 5 a introduit une autre méthode appelée bind() en plus de call() et apply() pour définir explicitement this valeur de la fonction sur un objet spécifique.

Il se comporte différemment des deux autres. Le premier argument de bind() est la valeur this pour la nouvelle fonction. Tous les autres arguments représentent des paramètres nommés qui doivent être définis de manière permanente dans la nouvelle fonction.

function showName(label) {
    console.log(label + ":" + this.name);
}
var student1 = {
     name: "Ravi"
};
var student2 = {
     name: "Vinod"
};

// create a function just for student1
var showNameStudent1 = showName.bind(student1);
showNameStudent1("student1"); // outputs "student1:Ravi"

// create a function just for student2
var showNameStudent2 = showName.bind(student2, "student2");
showNameStudent2(); // outputs "student2:Vinod"

// attaching a method to an object doesn't change `this` value of that method.
student2.sayName = showNameStudent1;
student2.sayName("student2"); // outputs "student2:Ravi"

Paramètres par défaut

Avant ECMAScript 2015 (ES6), la valeur par défaut d'un paramètre pouvait être affectée de la manière suivante:

function printMsg(msg) {
  msg = typeof msg !== 'undefined' ? // if a value was provided 
        msg :                        // then, use that value in the reassignemnt
        'Default value for msg.';    // else, assign a default value
  console.log(msg);
}

ES6 a fourni une nouvelle syntaxe où la condition et la réaffectation décrites ci-dessus ne sont plus nécessaires:

6
function printMsg(msg='Default value for msg.') {
    console.log(msg);
}
printMsg(); // -> "Default value for msg."
printMsg(undefined); // -> "Default value for msg."
printMsg('Now my msg in different!'); // -> "Now my msg in different!"

Cela montre également que si un paramètre est manquant lorsque la fonction est appelée, sa valeur reste undefined , comme cela peut être confirmé en le fournissant explicitement dans l'exemple suivant (en utilisant une fonction de flèche ):

6
let param_check = (p = 'str') => console.log(p + ' is of type: ' + typeof p);

param_check(); // -> "str is of type: string"
param_check(undefined); // -> "str is of type: string"

param_check(1); // -> "1 is of type: number"
param_check(this); // -> "[object Window] is of type: object"

Fonctions / variables en tant que valeurs par défaut et paramètres de réutilisation

Les valeurs des paramètres par défaut ne sont pas limitées aux nombres, aux chaînes ou aux objets simples. Une fonction peut également être définie comme valeur par défaut callback = function(){} :

6
function foo(callback = function(){ console.log('default'); }) {
    callback();
}

foo(function (){
    console.log('custom');
});
// custom

foo();
//default

Certaines caractéristiques des opérations peuvent être effectuées via les valeurs par défaut:

  • Un paramètre précédemment déclaré peut être réutilisé comme valeur par défaut pour les valeurs des paramètres à venir.
  • Les opérations en ligne sont autorisées lors de l'attribution d'une valeur par défaut à un paramètre.
  • Les variables existant dans la même portée de la fonction en cours de déclaration peuvent être utilisées dans ses valeurs par défaut.
  • Les fonctions peuvent être appelées afin de fournir leur valeur de retour dans une valeur par défaut.
6
let zero = 0;
function multiply(x) { return x * 2;}

function add(a = 1 + zero, b = a, c = b + a, d = multiply(c)) {
    console.log((a + b + c), d);
}

add(1);                // 4, 4
add(3);                // 12, 12
add(2, 7);             // 18, 18
add(1, 2, 5);          // 8, 10
add(1, 2, 5, 10);      // 8, 20 

Réutiliser la valeur de retour de la fonction dans la valeur par défaut d'un nouvel appel:

6
let array = [1]; // meaningless: this will be overshadowed in the function's scope
function add(value, array = []) {
  array.push(value);
  return array;
}
add(5);         // [5]
add(6);         // [6], not [5, 6]
add(6, add(5)); // [5, 6]

valeur et longueur des arguments en l'absence de paramètres dans l'invocation

L' objet tableau arguments conserve uniquement les paramètres dont les valeurs ne sont pas par défaut, c'est-à-dire celles qui sont explicitement fournies lors de l'appel de la fonction:

6
function foo(a = 1, b = a + 1) {
    console.info(arguments.length, arguments);
    console.log(a,b);
}

foo();        // info: 0 >> []     | log: 1, 2
foo(4);       // info: 1 >> [4]    | log: 4, 5
foo(5, 6);    // info: 2 >> [5, 6] | log: 5, 6

Fonctions avec un nombre inconnu d'arguments (fonctions variadiques)

Pour créer une fonction qui accepte un nombre indéterminé d'arguments, il existe deux méthodes en fonction de votre environnement.

5

Chaque fois qu'une fonction est appelée, elle a un tableau comme arguments objet dans son champ d' application, contenant tous les arguments passés à la fonction. Indexer ou itérer sur ceci donnera accès aux arguments, par exemple

function logSomeThings() {
    for (var i = 0; i < arguments.length; ++i) {
        console.log(arguments[i]);
    }
}

logSomeThings('hello', 'world');
// logs "hello"
// logs "world"

Notez que vous pouvez convertir des arguments en un tableau réel si besoin est; voir: Conversion d'objets de type tableau en tableaux

6

A partir de l'ES6, la fonction peut être déclarée avec son dernier paramètre en utilisant l' opérateur de repos ( ... ). Cela crée un tableau contenant les arguments à partir de ce point

function personLogsSomeThings(person, ...msg) {
   msg.forEach(arg => {
       console.log(person, 'says', arg);
   });
}

personLogsSomeThings('John', 'hello', 'world');
// logs "John says hello"
// logs "John says world"

Les fonctions peuvent également être appelées de manière similaire, la syntaxe de propagation

const logArguments = (...args) => console.log(args)
const list = [1, 2, 3]

logArguments('a', 'b', 'c', ...list)
// output: Array [ "a", "b", "c", 1, 2, 3 ]

Cette syntaxe peut être utilisée pour insérer un nombre arbitraire d'arguments dans n'importe quelle position et peut être utilisée avec n'importe quelle itération ( apply n'accepte que les objets de type tableau).

const logArguments = (...args) => console.log(args)
function* generateNumbers() {
  yield 6
  yield 5
  yield 4
}

logArguments('a', ...generateNumbers(), ...'pqr', 'b')
// output: Array [ "a", 6, 5, 4, "p", "q", "r", "b" ]

Récupère le nom d'un objet fonction

6

ES6 :

myFunction.name

Explication sur MDN . À partir de 2015, fonctionne dans nodejs et tous les principaux navigateurs sauf IE.


5

ES5 :

Si vous avez une référence à la fonction, vous pouvez faire:

function functionName( func )
{
    // Match:
    // - ^          the beginning of the string
    // - function   the word 'function'
    // - \s+        at least some white space
    // - ([\w\$]+)  capture one or more valid JavaScript identifier characters
    // - \(         followed by an opening brace
    //
    var result = /^function\s+([\w\$]+)\(/.exec( func.toString() )
    
    return result ? result[1] : ''
}

Application partielle

Semblable au currying, une application partielle est utilisée pour réduire le nombre d'arguments transmis à une fonction. Contrairement au curry, le nombre n’a pas besoin d’être réduit.

Exemple:

Cette fonction ...

function multiplyThenAdd(a, b, c) {
    return a * b + c;
}

... peut être utilisé pour créer une autre fonction qui se multiplie toujours par 2, puis ajoute 10 à la valeur passée;

function reversedMultiplyThenAdd(c, b, a) {
    return a * b + c;
}

function factory(b, c) {
    return reversedMultiplyThenAdd.bind(null, c, b);
}

var multiplyTwoThenAddTen = factory(2, 10);
multiplyTwoThenAddTen(10); // 30

La partie "application" de l'application partielle signifie simplement la fixation des paramètres d'une fonction.

Composition de la fonction

Composer des fonctions multiples en une seule est une pratique courante de programmation fonctionnelle;

composition fait un pipeline à travers lequel nos données vont transiter et être modifiées simplement en travaillant sur la composition de la fonction (tout comme pour prendre des morceaux d'une piste ensemble) ...

vous commencez avec des fonctions à responsabilité unique:

6
 const capitalize = x => x.replace(/^\w/, m => m.toUpperCase());
 const sign = x => x + ',\nmade with love';

et créer facilement une piste de transformation:

6
 const formatText = compose(capitalize, sign);

 formatText('this is an example')
 //This is an example,
 //made with love

NB La composition est obtenue grâce à une fonction d'utilité généralement appelée compose comme dans notre exemple.

L'implémentation de compose est présente dans de nombreuses bibliothèques d'utilitaires JavaScript ( lodash , rambda , etc.) mais vous pouvez aussi commencer par une implémentation simple telle que:

6
 const compose = (...funs) =>
   x =>
   funs.reduce((ac, f) => f(ac), x);


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow