Aide-mémoire JS

Un mémo des manipulations courantes en JavaScript

a marqué ce sujet comme résolu.

Tu devinera que je parlais en ES5 et d'un truc destiné aux débutants, j'aurai peut être dû le préciser, en effet. Je ne crois pas qu'une personne qui utilise les fonctions fléchés ai besoin d'aide pour comprendre ce qu'est une closure si ? :p

+0 -1

Tu devinera que je parlais en ES5 et d'un truc destiné aux débutants, j'aurai peut être dû le préciser, en effet. Je ne crois pas qu'une personne qui utilise les fonctions fléchés ai besoin d'aide pour comprendre ce qu'est une closure si ? :p

Demandred

Je ne vois pas le rapport. Si tu ne comprends pas l'ES2015, je te la refais en ES5 :

1
var f = function () { return 1; }

Aussi, quand on écrit un truc destiné aux débutants, le but c'est qu'ils comprennent le concept, pas qu'ils apprennent un truc faux. Donc il vaut mieux leur expliquer c'est qu'est une closure avec 2 phrases et un exemple que les embrouiller en leur parlant de variables libres, en répétant trois fois des anneries du genre "toutes les fonctions sont des closures" alors que c'est pas le cas, etc.

Bref, si je devais expliquer à un débutant ce qu'est une closure, je donnerais cet exemple avec cette explication : ```

Un exemple minimal de closure serait plutôt ceci : (attention c'est très moche)

1
2
3
4
var x = 1;
function add1 (y) {
  return y + x;
}

Une closure est une fonction qui référence/utilise des variables qui n'appartiennent pas au scope de la fonction en question.

victor

Et comme tu peux pas expliquer ce qu'est une closure avant d'avoir abordé la notion de scope, tout va bien.

+0 -0

En fait tu réponds presque à ta propre erreur :

Toutes les fonctions sont des closures, même si on ne l'utilise pas toujours.

C'est pas vraiment ça.

N'importe quelle function peut être une closure si elle fait appel à des variables du scope de son parent.

Dans la pratique, la distinction est extrêmement importante. Savoir ce qu'est une closure et notamment par opposition à une fonction pure (pas de variables "externes" est nécessaire mais pas suffisant : pas d'effet de bord - écriture sur le disque, … -, pas d'appel à new Date(), random, …) permet de savoir sur quelles fonctions on peut faire de la mémoïsation ou non.

Et ça, quand on commence à aller chercher des perfs assez importantes (en JS : éviter des manipulations sur le DOM, côté serveur : répondre extrêmement vite sans désérialiser les données reçues, …) c'est extrêmement important.

La currification ou les fonctions de haut-niveau sont un exemple (très intéressant) d'utilisation des closures mais il me semble important de faire comprendre aux débutants ce qui fait le charme certes, mais aussi le danger des closures.

+0 -0

@EtienneR : Oui… mais non

  1. On peut faire ça en CSS pur avec une checkbox cachée
  2. Ce HTML n’est pas accessible (span au lieu de button, div au lieu de nav…)
  3. Tu interceptes la touche Esc qui peut avoir d’autres usages dans le documents
  4. Plutôt que d’ajouter une bête class, autant utiliser l’attribut hidden et/ou aria-hidden
  5. onkeyup, c’était bien il y a 10 ans, maintenant on préfère ajouter un EventListener proprement ;)

Rien sur es6?

Fonctions
1
2
3
4
let myFunction = (firstArg, secondArg='world') => {
    console.log(`Le premier argument est: ${firstArg}, et le deuxieme est ${secondArg}`)
}
myFunction('hello') // Le premier argument est: hello, et le deuxieme est world

Ces fonctions flechee on sont differentes des "normales": on ne peux pas changer sa valeur de this.

1
2
3
document.body.addEventListener('click', e => {
    console.log(this)
})

Quand on click sur body, ca log l’object window (parce que au moment ou j’ai creer ma fonction, this avait cette valeur)

C’est tres pratique quand on travaille avec des classes, et des nested evenements.

Si il n’y a qu’un seul argument, vous n’etes pas oblige de mettre des paranthese.

Récupérer les valeurs d’un select

Select simple

1
2
3
4
5
6
<select name="agrumes">
    <option value="citron">citron</option>
    <option value="clementine">clémentine</option>
    <option value="orange">orange</option>
    <option value="pamplemousse">pamplemousse</option>
</select>

On récupère la valeur avec la fonction change dans addEventListener.

1
2
const el = document.querySelector('[name="agrumes"]')
el.addEventListener('change', () => console.log(el.value))

Mutliselect

1
2
3
4
5
6
<select name="agrumes" multiple>
    <option value="citron">citron</option>
    <option value="clementine">clémentine</option>
    <option value="orange">orange</option>
    <option value="pamplemousse">pamplemousse</option>
</select>

On récupère la ou les valeur(s) dans un tableau.

1
2
3
4
5
6
7
const el = document.getElementsByName('agrumes')[0]
el.addEventListener('change', () => console.log(
  Array
    .from(el.options)
    .filter(option => option.selected)
    .map(option => option.value))
)

Edit : prise en compte des remarques de victor et de viki53. Merci à vous :)

+0 -1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// pas de raison que ce soit `let`
let el = document.getElementsByName('agrumes')[0]
// pas nécessaire d'avoir `function` au lieu d'une arrow function
el.onchange = function () {
    // pas besoin de tout ça mais commentons quand même :
    let result = [] // `const` à nouveau, pas let
    for (i in el.options) { // attention, `i` n'est pas déclaré, si y'a un `i` dans le scope parent…
        if (el.options[i].selected) {
            result.push(el.options[i].value)
        }
    }
    console.table(result)
}

// =>

const el = document.getElementsByName('agrumes')[0]
el.onchange = () =>
  Array
    .from(el.options)
    .filter(option => option.selected)
    .map(option => option.value)

// et si tu tiens à `console.log`
el.onchange = () => console.log(
  Array
    .from(el.options)
    .filter(option => option.selected)
    .map(option => option.value))

;)

(Je fais pas les autres parce que bon, un exemple suffit !)

[edit] Boarf remontons un peu quand même :

Ces fonctions flechee on sont differentes des "normales": on ne peux pas changer sa valeur de this.

1
2
3
document.body.addEventListener('click', e => {
    console.log(this)
})

Quand on click sur body, ca log l’object window (parce que au moment ou j’ai creer ma fonction, this avait cette valeur)

C’est pas une question de changer this, on peut très bien le faire :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(function () {
  console.log(`${this.a} ${this.b}`) // undefined undefined
  this.a = "foo"
  const x = () => {
    this.a += " bar"
    this.b = "baz"
  }
  x()
  console.log(`${this.a} ${this.b}`) // foo bar baz
})()

Et si ça log window dans ton cas, c’est parce que dans un navigateur this est toujours window dans le scope de base, et t’as probablement tapé ça dans la console de ton navigateur.

Une arrow function ne bind jamais this, donc this est toujours celui du premier scope parent bindant this.

+2 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte