# Leçon: Fonctions
## 1. Fonctions
Dans cette leçon, nous allons aborder les mises à jour des fonctions dans es6. Les fonctions ont beaucoup changé depuis la dernière version de JavaScript.
Nous avons maintenant une nouvelle façon d'écrire des fonctions appelées fonctions fléchées ('name => name.toUpperCase()') et un nouveau mot-clé 'class' qui vous permet de créer des fonctions en tant que classes ('class Cone { }').
Ce n'est pas tout. Dans es6, vous pouvez maintenant définir les paramètres de fonction par défaut et vous pouvez connecter différentes classes entre elles en utilisant les nouveaux mots-clés 'super' et 'extended'.
```js
class MintCode extends Cone {
constructor(data) {
super(data);
this.flavor = 'Mint';
}
}
```
La première chose que nous allons examiner, ce sont les fonctions fléchées.
## 2. Fonctions fléchées
Les fonctions sont l'une des principales structures de données en JavaScript ; Ils existent depuis _toujours_.
### Les fonctions fléchées ES6 introduisent un nouveau type de fonction appelée **fonction flèchée**.
Les fonctions fléchées sont très similaires aux fonctions régulières dans le comportement, mais sont assez différentes syntaxiquement. Le code suivant prend une liste de noms et convertit chacun d'entre eux en majuscules à l'aide d'une fonction normale :
```js
const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map(function(name) {
return name.toUpperCase();
});
```
Le code ci-dessous fait la même chose sauf qu'au lieu de passer une fonction régulière à la méthode `map()`, il passe une fonction fléchée. Notez la flèche dans la fonction flèche ( `=>` ) dans le code ci-dessous :
```js
const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map(
name => name.toUpperCase()
);
```
> **REMARQUE :** Vous ne savez pas comment fonctionne `map()` ? C'est une méthode sur le prototype Array. Vous lui transmettez une fonction et il appelle cette fonction une fois sur chaque élément du tableau.
> Il rassemble ensuite les valeurs renvoyées par chaque appel de fonction et crée un nouveau tableau avec ces résultats.
> Pour plus d'informations, consultez la documentation [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
### Convertir une fonction en fonction fléchée
```js
const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map(function(name) {
return name.toUpperCase();
});
```
1. supprimez le mot-clé `function`
2. supprimer les parenthèses
3. supprimer les accolades d'ouverture et de fermeture
4. supprimez le mot-clé `return`
5. supprimez le point-virgule
6. ajoutez une flèche ( `=>` ) entre la liste des paramètres et le corps de la fonction
```js
// es5
const upperizedNames = ['Farrin', 'Kagure', 'Asser']
.map(function(name) {
return name.toUpperCase();
});
// es6
const upperizedNames = ['Farrin', 'Kagure', 'Asser']
.map( name => name.toUpperCase() );
```
#### Quiz
Analysez le code suivant:
```js
const names = ['Afghanistan', 'Aruba', 'Bahamas', 'Chile', 'Fiji', 'Gabon',
'Luxembourg', 'Nepal', 'Singapore', 'Uganda', 'Zimbabwe'];
const longNames = names.filter(function(name) {
return name.length > 6;
});
```
Lequel des choix suivants fait la même chose, mais remplace la fonction de .filter() par une fonction fléchée ?
1. [ ] const longNames = names.filter( function(name) => return name.length > 6; );
2. [ ] const longNames = names.filter( return name.length > 6 );
3. [ ] const longNames = names.filter( name => {names.length > 6} );
4. [ ] const longNames = names.filter( name => name.length > 6 );
#### Solution
Cette fonction fléchée renvoie les noms de pays comportant six caractères ou plus.
- [x] const longNames = names.filter( name => name.length > 6 );
## 3. Utiliser les fonctions fléchées
Les fonctions régulières peuvent être soit des **déclarations de fonction**, soit des **expressions de fonction**, cependant les **fonctions fléchées sont _toujours des_ expressions**. En fait, leur nom complet est « expressions de fonction fléchée », elles ne peuvent donc être utilisées que là où une expression est valide. Cela inclut être :
- stocké dans une variable,
- passé en argument à une fonction,
- et stocké dans la propriété d'un objet.
Une syntaxe déroutante est celle où une fonction fléchée est stockée dans une variable.
```js
const greet = name => `Hello ${name}!`;
```
Dans le code ci-dessus, la fonction flèchée est stockée dans la variable `greet` et vous l'appelleriez comme this :
```js
greet('James');
```
> **Renvoie:** Hello James!
### Parenthèses et paramètres
Vous avez peut-être remarqué que la fonction flèche de la fonction greet() ressemble à ceci :
```js
name => `Hello ${name}!`
```
Si vous vous en souvenez, la liste des paramètres apparaît avant la flèche de la fonction flèche (c'est-à-dire `=>`).
S'il n'y a qu'**un** paramètre dans la liste, vous pouvez l'écrire comme dans l'exemple ci-dessus. Mais, s'il y a **deux éléments ou plus** dans la liste de paramètres, ou s'il y a **zéro** éléments dans la liste, vous devez alors mettre la liste entre parenthèses :
```js
// empty parameter list requires parentheses
const sayHi = () => console.log('Hello Student!');
sayHi();
```
> **Affiche:** Hello Student!
```js
const orderIceCream = (flavor, cone) => console.log(`Voici votre glace ${flavor} dans un cone en ${cone}.`);
orderIceCream('chocolate', 'gaufre');
```
> **Affiche:** Voici votre glace chocolat dans un cone en gaufre.
#### Question 1 sur 2
Parmi les choix suivants, lesquels ont des fonctions fléchées correctement formatées ?
1. [ ]
```js
setTimeout(() => {
console.log('starting the test');
test.start();
}, 2000);
```
2. [ ]
```js
setTimeout( _ => {
console.log('starting the test');
test.start();
}, 2000);
```
3. [ ]
```js
const vowels = 'aeiou'.split('');
const bigVowels = vowels.map( (letter) => letter.toUpperCase() );
```
4. [ ]
```js
const vowels = 'aeiou'.split('');
const bigVowels = vowels.map( letter => letter.toUpperCase() );
```
#### Solution
En fait, chacun de ces éléments est correct.
1. [x] setTimeout(**()** => {
1. [x] setTimeout( **_** => {
1. [x] const bigVowels = vowels.map( **(letter)** => letter.toUpperCase() );
1. [x] const bigVowels = vowels.map( **letter** => letter.toUpperCase() );
S'il n'y a aucun paramètre dans la fonction, vous utilisez simplement une paire de parenthèses vides comme l'option 1.
Alternativement, certains développeurs choisissent d'utiliser un trait de soulignement comme paramètre unique. Le trait de soulignement n'est jamais utilisé, il est donc « non défini » à l'intérieur de la fonction, mais c'est une technique courante.
La seule différence entre les options 3 et 4 est l'utilisation des parenthèses autour de « lettre ». Généralement, s'il n'y a qu'un seul paramètre, aucune parenthèse n'est utilisée, mais ce n'est pas faux.
### Syntaxe de corps concise et bloc
Toutes les fonctions fléchées que nous avons examinées n'avaient qu'une seule expression comme corps de fonction :
```js
const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map(
name => name.toUpperCase()
);
```
Ce format du corps de la fonction est appelé _"syntaxe du corps concise"_. La syntaxe concise :
- n'a pas d'accolades entourant le corps de la fonction
- et renvoie automatiquement l'expression.
Si vous avez besoin de plus qu'une simple ligne de code dans le corps de votre fonction fléchée, vous pouvez utiliser la _"syntaxe du corps de bloc"_.
```js
const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map( name => {
name = name.toUpperCase();
return `${name} has ${name.length} characters in their name`;
});
```
Points importants à garder à l’esprit concernant la syntaxe des blocs :
il utilise des accolades pour envelopper le corps de la fonction
et une instruction « return » doit être utilisée pour renvoyer réellement quelque chose de la fonction.
#### Question 2 sur 2
En utilisant vos connaissances sur le fonctionnement des fonctions fléchées avec les retours automatiques et les accolades, lesquels des choix suivants ont des fonctions fléchées correctement formatées ?
1. [ ]
```js
const colors = ['red', 'blue', 'yellow', 'orange', 'black'];
const crazyColors = colors.map( color => {
const jumble = color.split('').reverse();
return jumble.join('') + '!';
});
```
2. [ ]
```js
const colors = ['red', 'blue', 'yellow', 'orange', 'black'];
const crazyColors = color.map( color => {
color.split('').reverse().join('') + '!';
});
```
3. [ ]
```js
const colors = ['red', 'blue', 'yellow', 'orange', 'black'];
const crazyColors = color.map( color => return color.split('').reverse().join('') + '!' );
```
4. [ ]
```js
const colors = ['red', 'blue', 'yellow', 'orange', 'black'];
const crazyColors = color.map( color => color.split('').reverse().join('') + '!' );
```
#### Solution
Les options 1 et 4 utilisent toutes deux une syntaxe correcte pour les fonctions fléchées.
1. [x] L'option 1 est correcte. Étant donné que la fonction flèche utilise des accolades, il doit y avoir un « retour » quelque part pour que quelque chose soit réellement renvoyé.
1. [ ] L'option 2 n'est pas correcte car elle comporte des accolades et aucun « retour ». Cette fonction s'exécute, mais rien n'est renvoyé à crazyColors.
1. [ ] L'option 3 n'a pas d'accolades. Cela signifie qu'il doit être dans une syntaxe concise et renvoyer automatiquement l'expression afin qu'il ne doive pas avoir de mot-clé `return`, donc celui-ci n'est pas correct.
1. [x] L'option 4 est correcte. C'est la manière la plus courante d'écrire les fonctions fléchées, sous forme de lignes simples qui reviennent automatiquement.
Les fonctions fléchées sont donc géniales !
- La syntaxe est beaucoup plus courte,
- il est plus facile d'écrire et de lire des fonctions courtes sur une seule ligne,
- et ils reviennent automatiquement lors de l'utilisation de la syntaxe de corps concise !
> **AVERTISSEMENT :** Mais tout n'est pas toujours de tout repos, et il y a certainement des moments où vous ne voudrez peut-être pas utiliser une fonction de flèche. Donc, avant d'effacer de votre mémoire comment écrire une fonction traditionnelle, vérifiez ces implications :
>
> - il y a un piège avec le mot-clé `this` dans les fonctions fléchées
> - passez à la leçon suivante pour connaître les détails !
>
> - les fonctions fléchées ne sont que des _expressions_
> - il n'existe pas de déclaration de fonction flèche
## 4. Quiz : Convertir en fonction fléchée
### Instructions:
Convertissez la fonction passée à la méthode map() en fonction flèche.
####Code
```js
// convert to an arrow function
const squares = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(function(square) {
return square * square;
});
console.log(...squares);
```
#### Solution
```js
const squares = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(square => square * square);
console.log(...squares);
```
## 5. Recap sur les fonctions fléchées
Les fonctions fléchées sont géniales et peuvent vraiment nettoyer votre code en supprimant :
- le mot-clé `function`
- le mot clé `return`
- accolades ( `{}` )
Mais n'allez pas encore convertir toutes les fonctions en fonctions fléchées, car il y a un gros piège avec les fonctions fléchées.
La façon dont les fonctions fléchées gèrent le mot-clé « this » est différente des fonctions normales.
## 6. Fonctions fléchées et mot-clé « this »
« this » peut être assez déroutant car il s'agit d'une valeur dynamique qui est différente selon la façon dont une fonction est appelée.
Avec les fonctions régulières, la valeur de « this » dépend de la manière dont la fonction est appelée.
Avec les fonctions fléchées, cela dépend de l'endroit où se trouve cette fonction dans le code.
Nous passerons brièvement en revue le fonctionnement du mot-clé « this » en général, puis nous examinerons comment il fonctionne dans les fonctions fléchées.
## 7. 'this' et les fonctions régulières
Pour comprendre comment « ceci » fonctionne différemment avec les fonctions fléchées, faisons un bref récapitulatif de la façon dont « ceci » fonctionne dans une fonction standard.
La valeur du mot-clé `this` dépend entièrement de la façon dont sa fonction (ou méthode) est appelée. `this` pourrait être l'un des éléments suivants :
---
### 1. Un nouvel objet
Si la fonction est appelée avec `new` :
```js
const mySundae = new Sundae('Chocolate', ['Sprinkles', 'Hot Fudge']);
```
Dans le code ci-dessus, la valeur de « this » à l'intérieur de la fonction constructeur « Sundae » est un nouvel objet car il a été appelé avec « new ».
---
### 2. Un objet spécifié
Si la fonction est invoquée avec `call/apply` :
```js
const result = obj1.printName.call(obj2);
```
Dans le code ci-dessus, la valeur de `this` à l'intérieur de `printName()` fera référence à `obj2` puisque le premier paramètre de `call()` est de définir explicitement à quoi `this` fait référence.
---
### 3. Un objet contextuel
Si la fonction est une méthode d'un objet :
```js
data.teleport();
```
Dans le code ci-dessus, la valeur de « this » à l'intérieur de « teleport() » fera référence à l'objet « data ».
---
### 4. L'objet global ou non défini
Si la fonction est appelée sans contexte :
```js
teleport();
```
Dans le code ci-dessus, la valeur de « this » à l'intérieur de « teleport() » est soit l'objet global, soit, si en mode strict, elle est « non définie ».
---
#### Question 1 de 2
Quelle est la valeur de « this » dans la fonction constructeur « Train » ci-dessous ?
```js
const redTrain = new Train('red');
```
1. [ ] the `window` object
1. [ ] a new object
1. [ ] `undefined`
#### Solution
Puisque le mot-clé new a été utilisé, la bonne réponse est un nouvel objet.
- [x] un nouvel objet
#### Question 2 sur 2
Quelle est la valeur de « this » dans la fonction « increaseSpeed() » ci-dessous ?
```js
const redTrain = new Train('red');
redTrain.increaseSpeed(25);
```
1. [ ] the `window` object
1. [ ] a new object
1. [ ] the `redTrain` object
1. [ ] `undefined`
#### Solution
Puisque la fonction `increaseSpeed()` est appelée à partir d'un objet contextuel (`redTrain`), cet objet contextuel aura la valeur de `this` dans la fonction.
- [x] l'objet `redTrain`
## 8. Fonctions fléchées et 'this'
Avec les fonctions régulières, la valeur de « this » est définie en fonction de _la manière dont la fonction est appelée_. Avec les fonctions fléchées, la valeur de « this » est basée sur le contexte environnant de la _fonction_. En d'autres termes, la valeur de « ceci » _à l'intérieur_ d'une fonction fléchée est la même que la valeur de « ceci » _à l'extérieur_ de la fonction.
Voyons un exemple avec « this » dans les fonctions normales, puis regardons comment les fonctions fléchées fonctionneront.
```js
// constructor
function IceCream() {
this.scoops = 0;
}
// adds scoop to ice cream
IceCream.prototype.addScoop = function() {
setTimeout(function() {
this.scoops++;
console.log('scoop added!');
}, 500);
};
const dessert = new IceCream();
dessert.addScoop();
```
> **Affiche:**<br>
> scoop added!
Après avoir exécuté le code ci-dessus, vous _pensez_ que « dessert.scoops » serait 1 après une demi-seconde. Mais malheureusement ce n'est pas le cas :
```js
console.log(dessert.scoops);
```
> **Affiche:**<br>
> 0
Pouvez-vous dire pourquoi ?
La fonction passée à `setTimeout()` est appelée sans `new`, sans `call()`, sans `apply()` et sans objet contextuel. Cela signifie que la valeur de « this » à l'intérieur de la fonction est l'objet global et **PAS** l'objet « dessert ». Donc, ce qui s'est réellement passé, c'est qu'une nouvelle variable `scoops` a été créée (avec une valeur par défaut de `undefined`) et a ensuite été incrémentée (`undefined + 1` donne `NaN`) :
```js
console.log(scoops);
```
> **Prints:**<br>
> NaN
Une solution consiste à utiliser la fermeture (closure) :
```js
// constructor
function IceCream() {
this.scoops = 0;
}
// adds scoop to ice cream
IceCream.prototype.addScoop = function() {
const cone = this; // sets `this` to the `cone` variable
setTimeout(function() {
cone.scoops++; // references the `cone` variable
console.log('scoop added!');
}, 0.5);
};
const dessert = new IceCream();
dessert.addScoop();
```
Le code ci-dessus _fonctionnera_ car au lieu d'utiliser « this » à l'intérieur de la fonction, il définit la variable cone sur this, puis recherche la variable cone lorsque la fonction est appelée. Cela fonctionne car il utilise la valeur de this en dehors de la fonction. Donc, si nous vérifions le nombre de boules dans notre dessert en ce moment, nous verrons la valeur correcte de « 1 » :
```js
console.log(dessert.scoops);
```
> **Affiche:**<br>
> 1
Eh bien, c'est exactement ce que font les fonctions fléchées, alors remplaçons la fonction passée à `setTimeout()` par une fonction fléchée :
```js
// constructor
function IceCream() {
this.scoops = 0;
}
// adds scoop to ice cream
IceCream.prototype.addScoop = function() {
setTimeout(() => { // an arrow function is passed to setTimeout
this.scoops++;
console.log('scoop added!');
}, 0.5);
};
const dessert = new IceCream();
dessert.addScoop();
```
Puisque les fonctions fléchées héritent de leur valeur this du contexte environnant, ce code fonctionne !
```js
console.log(dessert.scoops);
```
> **Affiche:**<br>
> 1
Lorsque `addScoop()` est appelé, la valeur de `this` _dans_ `addScoop()` fait référence à `dessert`. Puisqu'une fonction fléchée est passée à `setTimeout()`, elle utilise son contexte environnant pour déterminer à quoi `this` fait référence à l'intérieur d'elle-même. Ainsi, puisque `this` _à l'extérieur_ de la fonction fléchée fait référence à `dessert`, la valeur de `this` _à l'intérieur_ de la fonction fléchée fera également référence à `dessert`.`
Maintenant, que pensez-vous qu'il se passerait si nous changeions la méthode `addScoop()` en une fonction fléchée ?
```js
// constructor
function IceCream() {
this.scoops = 0;
}
// adds scoop to ice cream
IceCream.prototype.addScoop = () => { // addScoop is now an arrow function
setTimeout(() => {
this.scoops++;
console.log('scoop added!');
}, 0.5);
};
const dessert = new IceCream();
dessert.addScoop();
```
Ouais, cela ne fonctionne pas pour la même raison : les fonctions fléchées héritent de leur valeur « this » de leur contexte environnant. En dehors de la méthode `addScoop()`, la valeur de `this` est l'objet global. Donc, si `addScoop()` est une fonction fléchée, la valeur de `this` _inside_ `addScoop()` est l'objet global. Ce qui rend alors la valeur de « this » dans la fonction passée à « setTimeout() » également définie sur l'objet global !
## 9. Paramètres de fonction par défaut
Jetez un coup d'oeil à ce code :
```js
function greet(name, greeting) {
name = (typeof name !== 'undefined') ? name : 'Student';
greeting = (typeof greeting !== 'undefined') ? greeting : 'Welcome';
return `${greeting} ${name}!`;
}
greet(); // Welcome Student!
greet('James'); // Welcome James!
greet('Richard', 'Howdy'); // Howdy Richard!
```
> **Renvoie:**
> Welcome Student!
> Welcome James!
> Howdy Richard!
Qu'est-ce que c'est que tout cet horrible désordre dans les deux premières lignes de la fonction `greet()` ? Tout cela est là pour fournir des valeurs par défaut à la fonction si les arguments requis ne sont pas fournis. C'est quand même assez moche...
Heureusement, ES6 a introduit une nouvelle façon de créer des valeurs par défaut. C'est ce qu'on appelle _paramètres de fonction par défaut_.
### Paramètres de fonction par défaut
**Les paramètres de fonction par défaut** sont assez faciles à lire puisqu'ils sont placés dans la liste des paramètres de la fonction :
```js
function greet(name = 'Student', greeting = 'Welcome') {
return `${greeting} ${name}!`;
}
greet(); // Welcome Student!
greet('James'); // Welcome James!
greet('Richard', 'Howdy'); // Howdy Richard!
```
> **Renvoie:**
> Welcome Student!
> Welcome James!
> Howdy Richard!
C'est beaucoup moins de code, tellement plus propre et beaucoup plus facile à lire !
Pour créer un paramètre par défaut, vous ajoutez un signe égal ( `=` ) puis ce que vous voulez que le paramètre ait par défaut si aucun argument n'est fourni. Dans le code ci-dessus, les deux paramètres ont des valeurs de chaînes par défaut, mais ils peuvent être de n'importe quel type JavaScript !
#### Quiz
Ananlysez le code suivant :
```js
function shippingLabel(name, address) {
name = (typeof name !== 'undefined') ? name : 'Richard';
address = (typeof address !== 'undefined') ? address : 'Mountain View';
return `To: ${name} In: ${address}`;
}
```
Lequel des choix suivants est la bonne façon d’écrire la fonction shippingLabel() en utilisant les paramètres de fonction par défaut ?
1. [ ]
```js
function shippingLabel(name = '', address = '') {
return `To ${name} In: ${address}`;
}
```
2. [ ]
```js
function shippingLabel(name, address) {
name = name || 'Richard';
address = address || 'Mountain View';
return `To: ${name} In: ${address}`;
}
```
3. [ ]
```js
function shippingLabel(name, address) {
return `To: ${name} In: ${address}`;
}
```
4. [ ]
```js
function shippingLabel(name = 'Richard', address = 'Mountain View') {
return `To: ${name} In: ${address}`;
}
```
#### Solution
L'option 4 utilise correctement les paramètres de fonction par défaut en définissant les valeurs par défaut directement sur les paramètres.
- [x]
```js
function shippingLabel(name = 'Richard', address = 'Mountain View') {
return `To: ${name} In: ${address}`;
}
```
## 10. Default et Destructuring
Vous pouvez combiner les paramètres de fonction par défaut avec le destructuring pour créer des fonctions assez puissantes !
```js
function createGrid([width = 5, height = 5]) {
return `Generates a ${width} x ${height} grid`;
}
createGrid([]); // Generates a 5 x 5 grid
createGrid([2]); // Generates a 2 x 5 grid
createGrid([2, 3]); // Generates a 2 x 3 grid
createGrid([undefined, 3]); // Generates a 5 x 3 grid
```
> **Renvoie:**
> Generates a 5 x 5 grid
> Generates a 2 x 5 grid
> Generates a 2 x 3 grid
> Generates a 5 x 3 grid
La fonction `createGrid()` s'attend à ce qu'un tableau lui soit transmis. Il utilise la déstructuration pour définir le premier élément du tableau sur la « largeur » et le deuxième élément sur la « hauteur ». Si le tableau est vide ou s'il ne contient qu'un seul élément, alors les paramètres par défaut entrent en jeu et donnent aux paramètres manquants une valeur par défaut de « 5 ».
Il y a cependant un problème avec cela, le code suivant ne fonctionnera pas :
```js
createGrid(); // throws an error
```
> **Uncaught TypeError :** Impossible de lire la propriété 'Symbol(Symbol.iterator)' de non défini
Cela génère une erreur car `createGrid()` s'attend à ce qu'un tableau soit transmis et il sera ensuite déstructuré. Puisque la fonction a été appelée sans passer de tableau, elle s'arrête. Mais nous pouvons utiliser les paramètres de fonction par défaut pour cela !
```js
function createGrid([width = 5, height = 5] = []) {
return `Generates a ${width} x ${height} grid`;
}
```
Vous voyez ce nouveau `= []` dans le paramètre de la fonction ? Si `createGrid()` est appelé sans aucun argument, alors il utilisera ce tableau vide par défaut. Et comme le tableau est vide, il n'y a rien à déstructurer en « largeur » et « hauteur », donc leurs valeurs par défaut s'appliqueront ! Ainsi, en ajoutant `= []` pour donner une valeur par défaut à l'ensemble du paramètre, le code suivant fonctionnera désormais :
```js
createGrid(); // Generates a 5 x 5 grid
```
> **Renvoie:** Generates a 5 x 5 grid
#### QUESTION 1 sur 2
Analysez le code suivant :
```js
function houseDescriptor([houseColor = 'green', shutterColors = ['red']]) {
return `I've a ${houseColor} house w/ ${shutterColors.join(' and ')} shutters`;
}
```
Lequel des choix suivants s’exécutera sans générer d’erreur ?
1. [ ] houseDescriptor('red', ['white', 'gray', 'pink']);
2. [ ] houseDescriptor(['green', ['white', 'gray', 'pink']]);
3. [ ] houseDescriptor(['blue', 'purple']);
4. [ ] houseDescriptor(['green]);
#### Solution
Les options 2 et 4 sont les seuls choix qui fonctionneront correctement sans générer d'erreur.
- [ ] Puisque `houseDescriptor` n'attend qu'un seul argument (un tableau) à transmettre, l'option 1 doit être incorrecte puisqu'elle appelle la fonction avec deux arguments.
- [x] L'option 2 est correcte.
- [ ] L'option 3 appelle la fonction avec un seul argument de tableau, mais le deuxième élément de la liste est une chaîne et `.join()` n'est pas une méthode de chaînes, donc le code renvoie une erreur.
- [x] L'option 4 est correcte.
### Valeurs par défaut et objets déstructurants
Tout comme la déstructuration de tableau avec les valeurs par défaut du tableau, une fonction peut avoir un objet comme paramètre par défaut et utiliser la déstructuration d'objet :
```js
function createSundae({scoops = 1, toppings = ['Hot Fudge']}) {
const scoopText = scoops === 1 ? 'scoop' : 'scoops';
return `Your sundae has ${scoops} ${scoopText} with ${toppings.join(' and ')} toppings.`;
}
createSundae({});
// Your sundae has 1 scoop with Hot Fudge toppings.
createSundae({scoops: 2});
// Your sundae has 2 scoops with Hot Fudge toppings.
createSundae({scoops: 2, toppings: ['Sprinkles']});
// Your sundae has 2 scoops with Sprinkles toppings.
createSundae({toppings: ['Cookie Dough']});
// Your sundae has 1 scoop with Cookie Dough toppings.
```
> **Renvoie:**
> Your sundae has 1 scoop with Hot Fudge toppings.
> Your sundae has 2 scoops with Hot Fudge toppings.
> Your sundae has 2 scoops with Sprinkles toppings.
> Your sundae has 1 scoop with Cookie Dough toppings.
Tout comme l'exemple de tableau précédent, si vous essayez d'appeler la fonction sans aucun argument, cela ne fonctionnera pas :
```js
createSundae(); // throws an error
```
> **Uncaught TypeError :** Impossible de correspondre à "undefined" ou "null".
Nous pouvons éviter ce problème en fournissant un objet par défaut à la fonction :
```js
function createSundae({scoops = 1, toppings = ['Hot Fudge']} = {}) {
const scoopText = scoops === 1 ? 'scoop' : 'scoops';
return `Your sundae has ${scoops} ${scoopText} with ${toppings.join(' and ')} toppings.`;
}
```
En ajoutant un objet vide comme paramètre par défaut au cas où aucun argument n'est fourni, l'appel de la fonction sans aucun argument fonctionne désormais.
```js
createSundae(); // Your sundae has 1 scoop with Hot Fudge toppings.
```
> **Renvoie:** Your sundae has 1 scoop with Hot Fudge toppings.
#### Question 2 sur 2
Analyser le code suivant:
```js
function houseDescriptor({houseColor = 'green', shutterColors = ['red']} = {}) {
return `I have a ${houseColor} house with ${shutterColors.join(' and ')} shutters`;
}
```
Lequel des choix suivants s’exécutera sans générer d’erreur ?
1. [ ] houseDescriptor({houseColor: 'red', shutterColors: ['white', 'gray', 'pink']});
2. [ ] houseDescriptor({houseColor: 'red'});
3. [ ] houseDescriptor();
4. [ ] houseDescriptor({shutterColors: ['orange', 'blue']});
5. [ ] houseDescriptor({});
#### Solution
En fait, chacun de ces appels de fonction fonctionnera correctement !
La seule option qui ne fonctionnerait PAS est :
- [ ] houseDescriptor({houseColor: 'red', shutterColors: 'white'});<br>
Uncaught TypeError: `.join` is not a function of String. The function is expecting an array as the `shutterColors` property.
### Valeurs par défaut tableau par rapport aux valeurs par défaut objets
Les paramètres de fonction par défaut sont un simple ajout, mais cela nous rend la vie tellement plus facile ! L'un des avantages des valeurs par défaut des objets par rapport aux valeurs par défaut des tableaux réside dans la manière dont elles gèrent les options ignorées. Regarde ça:
```js
function createSundae({scoops = 1, toppings = ['Hot Fudge']} = {}) { }
```
Avec la fonction `createSundae()` utilisant les valeurs par défaut des objets avec déstructuration, si vous souhaitez utiliser la valeur par défaut pour `scoops` mais changer les `toppings`, alors tout ce que vous avez à faire est de transmettre un objet avec `toppings` :
```js
createSundae({toppings: ['Hot Fudge', 'Sprinkles', 'Caramel']});
```
Comparez l'exemple ci-dessus avec la même fonction qui utilise les valeurs par défaut du tableau avec déstructuration.
```js
function createSundae([scoops = 1, toppings = ['Hot Fudge']] = []) { }
```
Avec cette configuration de fonction, si vous souhaitez utiliser le nombre de cuillères par défaut mais changer les garnitures, vous devrez appeler votre fonction un peu... bizarrement :
```js
createSundae([undefined, ['Hot Fudge', 'Sprinkles', 'Caramel']]);
```
Puisque les tableaux sont basés sur la position, nous devons passer « undéfini » pour « sauter » le premier argument (et accepter la valeur par défaut) pour accéder au deuxième argument.
**Sauf si vous avez de bonnes raisons d'utiliser les valeurs par défaut des tableaux avec la déstructuration des tableaux, nous vous recommandons d'opter pour les valeurs par défaut des objets avec la déstructuration des objets !**
## 11. Quiz : Paramètres de fonction par défaut
### Instructions:
Créez une fonction `buildHouse()` qui accepte un objet comme paramètre par défaut. L'objet doit définir les propriétés suivantes sur ces valeurs par défaut :
- `floors = 1`
- `color = 'red'`
- `walls = 'brick'`
La fonction doit renvoyer ce qui suit si aucun argument ou aucun objet vide n'est transmis à la fonction.
> Votre maison comporte 1 étage(s) avec des murs en briques rouges.
#### Code
```js
// your code goes here
// tests
console.log(buildHouse());
console.log(buildHouse({}));
console.log(buildHouse({floors: 3, color: 'yellow'}));
// Your house has 1 floor(s) with red brick walls.
// Your house has 1 floor(s) with red brick walls.
// Your house has 3 floor(s) with yellow brick walls.
```
#### Solution
```js
function buildHouse({floors = 1, color = 'red', walls = 'brick'} = {}) {
return `Your house has ${floors} floor(s) with ${color} ${walls} walls.`;
}
// tests
console.log(buildHouse());
console.log(buildHouse({}));
console.log(buildHouse({floors: 3, color: 'yellow'}));
// Your house has 1 floor(s) with red brick walls.
// Your house has 1 floor(s) with red brick walls.
// Your house has 3 floor(s) with yellow brick walls.
```
> **Remarque :** N'oubliez pas de renvoyer le résultat (comme indiqué ci-dessus) plutôt que `console.log()` depuis l'intérieur de la fonction.