|
| 1 | +JavaScript implémente le « duck typing ». On désigne ainsi un style de typage dynamique dans lequel ce sont les méthodes et propriétés d’un objet qui déterminent sa sémantique, plutôt que de se baser sur l’héritage d’une classe particulière ou l’implémentation d’une interface donnée. Le nom de ce concept vient du « test du canard », attribué à James Whitcomb Riley, qu’on peut formuler ainsi : |
| 2 | + |
| 3 | + > « Quand je vois un volatile qui marche comme un canard, nage comme un canard, et cancane comme un canard, alors j’appelle ce volatile un canard. » |
| 4 | +
|
| 5 | +En JavaScript, pour écrire des programmes robustes, nous avons parfois besoin de vérifier qu’un objet est conforme au type dont nous avons besoin. |
| 6 | + |
| 7 | +Nous pouvons utiliser `Object#hasOwnProperty()` pour détecter qu’un objet « a » une propriété définie sur lui-même, ce qu’on appelle une *propriété propre* (par opposition à une propriété hérité du prototype) : |
| 8 | + |
| 9 | +```js |
| 10 | +var duck = { |
| 11 | + quack: function() { |
| 12 | + console.log('quack') |
| 13 | + } |
| 14 | +} |
| 15 | + |
| 16 | +duck.hasOwnProperty('quack') // => true |
| 17 | +``` |
| 18 | + |
| 19 | +Nous n’avons toutefois pas équipé `duck` d’une méthode `hasOwnProperty()`, alors d’où vient-elle ? |
| 20 | + |
| 21 | +`duck` a été créé avec la syntaxe littérale `{…}`, qui définit un objet, de sorte qu’il hérite automatiquement de `Object.prototype` : |
| 22 | + |
| 23 | +```js |
| 24 | +var object = {quack: true} |
| 25 | + |
| 26 | +Object.getPrototypeOf(object) === Object.prototype // => true |
| 27 | +object.hasOwnProperty('quack') // => true |
| 28 | +``` |
| 29 | + |
| 30 | +Mais qu’en serait-il pour un objet qui n’hérite pas de `Object.prototype` ? |
| 31 | + |
| 32 | +```js |
| 33 | +// Créons un objet avec un prototype `null` |
| 34 | +var object = Object.create(null) |
| 35 | +object.quack = function() { |
| 36 | + console.log('quack') |
| 37 | +} |
| 38 | + |
| 39 | +Object.getPrototypeOf(object) === Object.prototype // => false |
| 40 | +Object.getPrototypeOf(object) === null // => true |
| 41 | + |
| 42 | +object.hasOwnProperty('quack') |
| 43 | +// => TypeError: Object object has no method 'hasOwnProperty' |
| 44 | +``` |
| 45 | + |
| 46 | +Nous pouvons toujours appeler la `hasOwnProperty()` de `Object.prototype`, ceci dit, du moment que nous l’appelons avec un `this` qui « ressemble à un objet ». `Function#call` nous permet d’appeler n’importe quelle fonction avec un `this` que nous contrôlons. |
| 47 | + |
| 48 | +```js |
| 49 | +// Le premier argument de `call` sera le `this` |
| 50 | +// Le reste des arguments est passé à la fonction |
| 51 | + |
| 52 | +Object.prototype.hasOwnProperty.call(object, 'quack') // => true |
| 53 | +``` |
| 54 | + |
| 55 | +# Défi |
| 56 | + |
| 57 | +Écrivez une fonction `duckCount()` qui inspecte les arguments qu’on lui passe et renvoie le nombre de ceux qui ont une propriété propre `quack` définie. Ignorez les propriétés hérités des prototypes. |
| 58 | + |
| 59 | +Exemple : |
| 60 | + |
| 61 | +```js |
| 62 | +var notDuck = Object.create({quack: true}) |
| 63 | +var duck = {quack: true} |
| 64 | +duckCount(duck, notDuck) // 1 |
| 65 | +``` |
| 66 | +## Arguments |
| 67 | + |
| 68 | +Vous recevrez un nombre variable d’arguments, d’un appel à l’autre. Chaque argument pourra être d’un type quelconque, avec des propriétés quelconques. Certains arguments auront une propriété `quack`, parfois héritée du prototype. Certains pourrons ne pas être équipés de `hasOwnProperty()`. |
| 69 | + |
| 70 | +## Conditions |
| 71 | + |
| 72 | +* N’utilisez ni boucle (`for`, `while`…) ni `Array.prototype.forEach` |
| 73 | +* Ne maintenez pas de variable pour le compteur / l’accumulateur. |
| 74 | +* Ne créez aucune fonction superflue |
| 75 | + |
| 76 | +## Conseil |
| 77 | + |
| 78 | +La variable automatique `arguments`, disponible dans toute fonction, est un *objet* qui ressemble à un tableau sans en être vraiment un : |
| 79 | + |
| 80 | +```js |
| 81 | +{ |
| 82 | + 0: 'argument0', |
| 83 | + 1: 'argument1', // etc. |
| 84 | + length: 2 |
| 85 | +} |
| 86 | +``` |
| 87 | + |
| 88 | +## Ressources |
| 89 | + |
| 90 | +* https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Function/call |
| 91 | +* https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/hasOwnProperty |
| 92 | +* https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Op%C3%A9rateurs/L_op%C3%A9rateur_in |
| 93 | +* https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/slice#Array-like |
| 94 | +* https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Fonctions/arguments |
| 95 | + |
| 96 | +## Base de travail |
| 97 | + |
| 98 | +```js |
| 99 | +function duckCount() { |
| 100 | + // VOTRE SOLUTION ICI |
| 101 | +} |
| 102 | + |
| 103 | +module.exports = duckCount |
| 104 | +``` |
0 commit comments