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.