Introduction au JavaScript

Programmation web - Client riche

Objectifs

  • Présentation du langage
  • Good parts vs bad parts
  • Introduction à l'utilisation de JS dans le navigateur

JavaScript

Les différentes parties

Le langage et ses API

L'environnement dans lequel on l'exécute (API spécifiques)

Ce qu'on va explorer ensemble

JS dans le navigateur : pour quoi faire ?

  • Manipulation dynamique de la page
  • Réaction à des événements utilisateur
  • Animations
  • Requêtes asynchrones
  • Dessin 2D/3D
  • Communication temps réel
  • pleiiiin d'autres choses...

Utiliser du JS dans une page web

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Hello world</title>
        <script type="module" src="./app.js"></script>
    </head>

    <body>
        <h1>Hello world</h1>
    </body>
</html>
monprojet
├── app.js
└── index.html

Eléments de syntaxe

// Les commentaires sur une seule ligne

/*
  les commentaires
  sur plusieurs lignes
*/

Les commentaires

// Déclaration de la variable
let myVariable

// Affectation d'une valeur
myVariable = "Hello, world !"

// Déclaration et affectation
let myFirstVar = "Hello"

// Réassignation de la valeur d'une variable
let myVariable = "Hello world !"
myVariable = "Vous voulez un whisky ?"

Les variables (1)

// Déclaration et affectation
const myVariable = "Hello, world !"

const hello = "Hello world !"
hello = "Vous voulez un whisky ?"
// => ReferenceError
// Impossible de réaffecter une constante

Les variables (2)

Vous verrez peut-être des scripts utiliser le mot-clef var pour déclarer une variable. C'est l'ancienne méthode. Oubliez totalement ce mot-clef, qui est aujourd'hui remplacé par let

Les types de données

JS est un langage à typage dynamique et faible.

Le type d'une variable est donc :

  • définit à l'exécution, en fonction de la donnée qui est stockée dedans
  • potentiellement variable au cours du temps (réassignation d'une variable avec une donnée dont le type est différent de la précédente)
const integer = 8000
const decimal = 123.456

// addition
8000 + 42 // => 8042
// soustraction
8000 - 42 // => 7958
// multiplication
13 * 37 // => 481
// division
8000 / 42 // => 190.476190476
// exponentiation
2 ** 3 // => 8 (2 puissance 3, quoi)

8000 / 0 // => Infinity
-8000 / 0 // => -Infinity
"Everything but a number" / 8000 // => NaN
"Everything but a number" / 8000 + 42 // => NaN

Number

"Hello, world !"
'Hello, world !'
`Hello, world !`

const who = "world";
"Hello, " + who + " !"
`Hello ${who} !`
// => "Hello, world !"

`The result of 1 + 1 is ${1 + 1}`
// => "The result of 1 + 1 is 2"

String

const ok = true
const notOk = false

Boolean

const nullValue = null

null / undefined (1)

let undefinedValue
console.log(undefinedValue) // => undefined

const explicitUndefined = undefined

On a le conteneur (la variable) et quelque chose de vide dedans (null)

On a le conteneur (la variable), mais absolument rien dedans (undefined)

null / undefined (2)

const me = {
  firstname: "Cyrille",
  age: 28
}

me.firstname // => "Cyrile"
me.age // => 28

me.firstname = "George"
me.firstname // => "George"

me.job = "developer and teacher"
me.job // "developer and teacher"

Object

const number = 123.456
number.toFixed(2) // => 123.46

const str = "Hello world !"
str.toUpperCase() // => HELLO WORLD !

Tout est objet

Structures conditionnelles

const name = prompt("What's your name ?")

if (name === "Cyrille") {
  alert("Me too !")
} else if (name === "Toto") {
  alert("Srsly?")
} else {
  alert(`Hello ${name} !`)
}
10 < 20 // => true
10 <= 20 // => true
20 > 10 // => true
20 >= 10 // => true
20 == 20 // => true
10 != 20 // => true

"hello" == "hello" // => true
"hello" != "world" // => true

Comparaison (1)

10 == "10" // => true
20 != "20" // => false

Comparaison (2)

😱

10 === "10" // => false
20 !== "20" // => true

🤗

Privilégier les opérateurs stricts (=== et !==) pour éviter la conversion implicite de type

true && true // => true
true && false // => false

true || true // => true
true || false // => true
false || false // => false

!true // => false
!false // => true

Opérateurs logiques (1)

10 && "hello" // => "hello"
0 && "hello" // => 0

10 || "hello" // => 10
0 || "hello" // => "hello"

!"hello" // => false

Opérateurs logiques (2)

Conversion automatique des valeurs en booléen afin de déterminer si la valeur "correspond" à true (valeur truthy) ou false (valeur falsy)

Renvoi de la première (||) ou de la dernière (&&) valeur truthy

let i = 0
while (i < 10) {
  console.log(i)
  ++i
}

let j = 0
do {
  console.log(j)
  ++j
} while (j < 10)

for (let k = 0; k < 10; ++k) {
  console.log(k)
}

// => 0 1 2 3 4 5 6 7 8 9

Boucles

Fonctions

const addTwoNumbers = (number1, number2) => {
  return number1 + number2
}

const addTwoNumbers = (number1, number2) => number1 + number2

addTwoNumbers(8000, 42) // => 8042

Fonctions (1)

const sayHello = (who = "World") => {
  return `Hello ${who}`
}

sayHello("Tom") // => Hello Tom
sayHello() // => Hello World
sayHello(null) // => Hello
sayHello(undefined) // => Hello World

Fonctions (2)

La valeur par défaut d'un paramètre est utilisée si ce paramètre reçoit la valeur undefined (explicitement ou implicitement)

const addNumbers = (number1) => {
  const addNumber1ToNumber2 = (number2) => {
    return number1 + number2
  }
  
  return addNumber1ToNumber2
}

const add3 = addNumbers(3)
add3(2) // => 5 (3 + 2)
addNumbers(3)(2) // => 5

Fonctions (3)

Une fonction crée à l'intérieur d'une autre fonction a accès au scope de sa fonction parent (même une fois que la fonction parent a return). On appelle cette fonction une "fermeture" ou "closure"

const applyFunctionOnValue = (value, f) => {
  return f(value)
}

const add2 = (n) => n + 2

applyFunctionOnValue(2, add2) // =>  4 (2 + 2)

applyFunctionOnValue(2, (n) => n + 2) // 4

Fonctions (4)

Les fonctions sont des valeurs comme les autres : on peut les passer en paramètre d'une fonction

Il existe aussi le mot-clef function pour définir une fonction. Il faut l'avoir en tête, mais nous n'utiliserons que les fonctions fléchées, parce qu'elles sont plus récentes et apportent moins de concepts obscurs et implicites avec elles

Tableaux

const emptyArray = []

const animals = ["cat", "dog", "mouse"]
animals[0] // => "cat"
animals[1] // => "dog"
animals[2] // => "mouse"
animals.length // => 3

animals[1] = "parrot"

animals.push("rat")
// => ["cat", "parrot", "mouse", "rat"] (modifie le tableau)

[...animals, "rat"]
// => ["cat", "parrot", "mouse", "rat"] (crée un nouveau tableau)

["rat", ...animals]
// => ["rat", "cat", "parrot", "mouse"] (crée un nouveau tableau)

animals.slice(0, 2)
// => ["cat", "parrot"] (nouveau tableau)

Tableaux (1)

const fruits = ["Apple", "Banana", "Strawberry"]

for (let i = 0, length = fruits.length; i < length; ++i) {
  console.log(fruits[i])
}

for (let fruit of fruits) {
  console.log(fruit)
}

fruits.forEach((fruit) => console.log(fruit))
// => "Apple" "Banana" "Strawberry"

fruits.map((fruit) => fruit.toUpperCase())
// => ["APPLE", "BANANA", "STRAWBERRY"] (nouveau tableau)

Tableaux (2)

Les modules

Les modules (1)

Un fichier JS est un module, et on peut définir ce que ce module expose au monde extérieur en exportant les valeurs qu'on veut :

export const add = (a, b) => a + b

export const difference = (a, b) => a - b

export const multiply = (a, b) => a * b

Les modules (2)

On peut ensuite importer ces valeurs à partir d'un autre module :

import { add, difference } from "./math.js"

const mySuperComplicatedFunction = () => {
  // do some complicated stuff
  
  const resultAdd = add(3, 2)
  const resultDifference = difference(2, 3)
}

Les modules (3)

Les exports d'un module peuvent être de deux types :

  • nommé : chaque valeur est exportée avec un nom, qui doit être utilisé lors de l'import
  • par défaut : export d'une seule valeur par module
const add = (a, b) => a + b

export default add
import add from "./math.js"

add(3, 2)

Nous préférerons nous contenter des exports nommés. Ils nous garantissent une meilleure stabilité dans les noms utilisés à travers les modules, donc une plus grande facilité de refactoring.

 

Les exports par défaut existent principalement pour des raisons de compatibilité avec le système de modules CommonJS, utilisé par NodeJS avant que les modules standards n'aient vu le jour

Nous n'utiliserons pas à 100% le système de modules natif, car l'implémentation n'est pas encore optimale dans les navigateurs. On utilisera la syntaxe, mais on utilisera un bundler (Parcel) pour compiler nos modules en bundles prêts à être envoyés dans le navigateur.

Récap !

  • JS peut à la fois être utilisé côté client (navigateur) et côté serveur : dans ce cours on se concentre sur le navigateur
  • JS permet d'ajouter de l'interactivité à une page web (événements, modifications dynamiques de la page, animations, requêtes asynchrones...)
  • La syntaxe de JS est relativement proche de à quoi vous êtes habitués, sauf au niveau du modèle objet
  • Le langage a des bons et des moins bons côtés : on va se concentrer sur les bons et éviter les moins bons (null/undefined, comparaison non-stricte, classes...)

Des questions ?

Programmation web - client riche - Introduction au JavaScript

By Cyrille Perois

Programmation web - client riche - Introduction au JavaScript

  • 1,209