# Conditions préalables

Ces articles sont destinés à vous aider à apprendre à utiliser three.js. Ils supposent que vous savez programmer en JavaScript. Ils supposent que vous savez ce qu'est le DOM, comment écrire du code HTML et comment créer des éléments DOM en JavaScript. Ils supposent que vous savez utiliser les modules es6 via import et via les balises `<script type="module">`. Ils supposent que vous savez utiliser les import maps. Ils supposent que vous connaissez un peu de CSS et que vous savez ce que sont les sélecteurs CSS. Ils supposent également que vous connaissez ES5, ES6 et peut-être un peu ES7. Ils supposent que vous savez que le navigateur n'exécute du JavaScript que via des événements et des callbacks. Ils supposent que vous savez ce qu'est une closure.

Voici quelques rappels et notes

## modules es6

Les modules es6 peuvent être chargés via le mot-clé `import` dans un script ou en ligne via une balise `<script type="module">`. Voici un exemple

    <script type="importmap">
    {
      "imports": {
        "three": "./path/to/three.module.js",
        "three/addons/": "./different/path/to/examples/jsm/"
      }
    }
    </script>

    <script type="module">
    import * as THREE from 'three';
    import {OrbitControls} from 'three/addons/controls/OrbitControls.js';

    ...

    </script>

Voir plus de détails au bas de cet article.

## `document.querySelector` et `document.querySelectorAll`

Vous pouvez utiliser `document.querySelector` pour sélectionner le premier élément qui correspond à un sélecteur CSS. `document.querySelectorAll` retourne tous les éléments qui correspondent à un sélecteur CSS.

## Vous n'avez pas besoin de `onload`

Beaucoup de pages vieilles de 20 ans utilisent du HTML comme ceci

    <body onload="somefunction()">

Ce style est obsolète. Mettez vos scripts au bas de la page.

    <html>
      <head>
        ...
      </head>
      <body>
         ...
      </body>
      <script>
        // javascript inline
      </script>
    </html>

ou utilisez la propriété `defer`.

## Savoir comment fonctionnent les closures

    function a(v) {
      const foo = v;
      return function() {
         return foo;
      };
    }

    const f = a(123);
    const g = a(456);
    console.log(f());  // imprime 123
    console.log(g());  // imprime 456

Dans le code ci-dessus, la fonction `a` crée une nouvelle fonction à chaque fois qu'elle est appelée. Cette fonction englobe la variable `foo`. Voici plus d'informations.

## Comprendre comment fonctionne `this`

`this` n'est pas magique. C'est effectivement une variable qui est automatiquement passée aux fonctions, tout comme un argument est passé à une fonction. L'explication simple est que lorsque vous appelez une fonction directement comme ceci

    somefunction(a, b, c);

`this` sera `null` (en mode strict ou dans un module), alors que lorsque vous appelez une fonction via l'opérateur point `.` comme ceci

    someobject.somefunction(a, b, c);

`this` sera défini sur `someobject`.

Là où les gens se perdent, c'est avec les callbacks.

     const callback = someobject.somefunction;
     loader.load(callback);

ne fonctionne pas comme une personne inexpérimentée pourrait s'attendre, car lorsque `loader.load` appelle le callback, il ne l'appelle pas avec l'opérateur point `.` donc par défaut `this` sera null (sauf si le loader le définit explicitement sur autre chose). Si vous voulez que `this` soit `someobject` lorsque le callback a lieu, vous devez le dire à JavaScript en le liant à la fonction.

     const callback = someobject.somefunction.bind(someobject);
     loader.load(callback);

Cet article pourrait aider à expliquer `this`.

## Éléments ES5/ES6/ES7

### `var` est obsolète. Utilisez `const` et/ou `let`

Il n'y a aucune raison d'utiliser `var` JAMAIS et à ce stade, il est considéré comme une mauvaise pratique de l'utiliser du tout. Utilisez `const` si la variable ne sera jamais réassignée, ce qui est la plupart du temps. Utilisez `let` dans les cas où la valeur change. Cela aidera à éviter des tonnes de bugs.

### Utilisez `for(elem of collection)` jamais `for(elem in collection)`

`for of` est nouveau, `for in` est ancien. `for in` avait des problèmes qui sont résolus par `for of`

Par exemple, vous pouvez itérer sur toutes les paires clé/valeur d'un objet avec

    for (const [key, value] of Object.entries(someObject)) {
      console.log(key, value);
    }

### Utilisez `forEach`, `map` et `filter` là où c'est utile

Les tableaux ont ajouté les fonctions `forEach`, `map` et `filter` et sont assez largement utilisés dans le JavaScript moderne.

### Utilisez la déstructuration

Supposons un objet `const dims = {width: 300, height: 150}`

ancien code

    const width = dims.width;
    const height = dims.height;

nouveau code

    const {width, height} = dims;

La déstructuration fonctionne aussi avec les tableaux. Supposons un tableau `const position = [5, 6, 7, 1]`;

ancien code

    const y = position[1];
    const z = position[2];

nouveau code

    const [, y, z] = position;

La déstructuration fonctionne également dans les arguments de fonction

    const dims = {width: 300, height: 150};
    const vector = [3, 4];

    function lengthOfVector([x, y]) {
      return Math.sqrt(x * x + y * y);
    }

    const dist = lengthOfVector(vector);  // dist = 5

    function area({width, height}) {
      return width * height;
    }
    const a = area(dims);  // a = 45000

### Utilisez les raccourcis de déclaration d'objet

ancien code

     const width = 300;
     const height = 150;
     const obj = {
       width: width,
       height: height,
       area: function() {
         return this.width * this.height
       },
     };

nouveau code

     const width = 300;
     const height = 150;
     const obj = {
       width,
       height,
       area() {
         return this.width * this.height;
       },
     };

### Utilisez le paramètre rest et l'opérateur spread `...`

Le paramètre rest peut être utilisé pour consommer un nombre quelconque de paramètres. Exemple

     function log(className, ...args) {
       const elem = document.createElement('div');
       elem.className = className;
       elem.textContent = args.join(' ');
       document.body.appendChild(elem);
     }

L'opérateur spread peut être utilisé pour étendre un itérable en arguments

    const position = [1, 2, 3];
    someMesh.position.set(...position);

ou copier un tableau

    const copiedPositionArray = [...position];
    copiedPositionArray.push(4); // [1,2,3,4]
    console.log(position); // [1,2,3] position n'est pas affectée

ou pour fusionner des objets

    const a = {abc: 123};
    const b = {def: 456};
    const c = {...a, ...b};  // c est maintenant {abc: 123, def: 456}

### Utilisez `class`

La syntaxe pour créer des objets de type classe avant ES5 était peu familière à la plupart des programmeurs. À partir d'ES5, vous pouvez maintenant utiliser le mot-clé `class` qui est plus proche du style C++/C#/Java.

### Comprendre les getters et setters

Les getters et setters sont courants dans la plupart des langages modernes. La syntaxe `class` d'ES5 les rend beaucoup plus faciles qu'avant ES5.

### Utilisez les fonctions fléchées (arrow functions) là où c'est approprié

C'est particulièrement utile avec les callbacks et les promises.

    loader.load((texture) => {
      // utiliser la texture
    });

Les fonctions fléchées lient `this` au contexte dans lequel vous créez la fonction fléchée.

    const foo = (args) => {/* code */};

est un raccourci pour

    const foo = (function(args) {/* code */}).bind(this));

Voir le lien ci-dessus pour plus d'informations sur `this`.

### Promises ainsi que async/await

Les Promises aident avec le code asynchrone. Async/await aident à utiliser les promises.

C'est un sujet trop vaste pour être abordé ici, mais vous pouvez lire sur les promises ici et sur async/await ici.

### Utilisez les littéraux de gabarit (Template Literals)

Les littéraux de gabarit sont des chaînes utilisant des accents graves (backticks) au lieu de guillemets.

    const foo = `this is a template literal`;

Les littéraux de gabarit ont fondamentalement 2 fonctionnalités. La première est qu'ils peuvent être multi-lignes

    const foo = `this
    is
    a
    template
    literal`;
    const bar = "this\nis\na\ntemplate\nliteral";

`foo` et `bar` ci-dessus sont identiques.

L'autre est que vous pouvez sortir du mode chaîne et insérer des fragments de JavaScript en utilisant `${javascript-expression}`. C'est la partie gabarit. Exemple :

    const r = 192;
    const g = 255;
    const b = 64;
    const rgbCSSColor = `rgb(${r},${g},${b})`;

ou

    const color = [192, 255, 64];
    const rgbCSSColor = `rgb(${color.join(',')})`;

ou

    const aWidth = 10;
    const bWidth = 20;
    someElement.style.width = `${aWidth + bWidth}px`;

# Apprenez les conventions de codage JavaScript.

Bien que vous soyez libre de formater votre code comme bon vous semble, il existe au moins une convention dont vous devriez être conscient. Les variables, noms de fonctions, noms de méthodes, en JavaScript sont tous en lowerCasedCamelCase. Les constructeurs, les noms de classes sont en CapitalizedCamelCase. Si vous suivez cette règle, votre code correspondra à la plupart des autres codes JavaScript. Beaucoup de linters, des programmes qui vérifient les erreurs évidentes dans votre code, vous signaleront des erreurs si vous utilisez la mauvaise casse, car en suivant la convention ci-dessus, ils peuvent savoir quand vous utilisez quelque chose de manière incorrecte.

    const v = new vector(); // clairement une erreur si toutes les classes commencent par une majuscule
    const v = Vector();     // clairement une erreur si toutes les fonctions commencent par une minuscule.

# Envisagez d'utiliser Visual Studio Code

Bien sûr, utilisez l'éditeur que vous voulez, mais si vous ne l'avez pas essayé, envisagez d'utiliser Visual Studio Code pour JavaScript et après l'avoir installé, configurez eslint. Cela pourrait prendre quelques minutes à configurer, mais cela vous aidera énormément à trouver les bugs dans votre JavaScript.

Quelques exemples

Si vous activez la règle `no-undef` alors VSCode via ESLint vous avertira de nombreuses variables non définies.

Ci-dessus, vous pouvez voir que j'ai mal orthographié `doTheThing` en `doThing`. Il y a un trait ondulé rouge sous `doThing` et en survolant, il me dit qu'il n'est pas défini. Une erreur évitée.

Si vous utilisez des balises `<script>` pour inclure three.js, vous recevrez des avertissements en utilisant `THREE`, alors ajoutez `/* global THREE */` en haut de vos fichiers JavaScript pour dire à eslint que `THREE` existe. (ou mieux, utilisez `import` 😉)

Ci-dessus, vous pouvez voir qu'eslint connaît la règle selon laquelle les `UpperCaseNames` sont des constructeurs et que vous devriez donc utiliser `new`. Une autre erreur détectée et évitée. C'est la règle `new-cap`.

Il existe des centaines de règles que vous pouvez activer ou désactiver ou personnaliser. Par exemple, j'ai mentionné ci-dessus que vous devriez utiliser `const` et `let` plutôt que `var`.

Ici, j'ai utilisé `var` et il m'a averti que je devrais utiliser `let` ou `const`

Ici, j'ai utilisé `let`, mais il a vu que je ne changeais jamais la valeur, alors il a suggéré que j'utilise `const`.

Bien sûr, si vous préférez continuer à utiliser `var`, vous pouvez simplement désactiver cette règle. Comme je l'ai dit ci-dessus, je préfère utiliser `const` et `let` plutôt que `var` car ils fonctionnent mieux et préviennent les bugs.

Dans les cas où vous avez vraiment besoin de outrepasser une règle, vous pouvez ajouter des commentaires pour les désactiver pour une seule ligne ou une section de code.

# Si vous avez vraiment besoin de prendre en charge les anciens navigateurs, utilisez un transpiler

La plupart des navigateurs modernes sont mis à jour automatiquement, donc l'utilisation de toutes ces fonctionnalités vous aidera à être productif et à éviter les bugs. Cela dit, si vous êtes sur un projet qui doit absolument prendre en charge les anciens navigateurs, il existe des outils qui prendront votre code ES5/ES6/ES7 et le transpileront vers du JavaScript pré-ES5.