par @borisschapira | Clever Age
Reveal.js en offre plusieurs:
Default -
Sky -
Beige -
Simple -
Serif -
Night
Moon -
Solarized
Les moteurs d’exécution JavaScript se séparent de plus en plus des navigateurs et deviennent des technologies stand-alone :
Du JavaScript embarqué dans de nombreux périphériques.
Exemple avec le microcontrôleur JavaScript de Tessel...
;
$
ou
_
. Utilisez-les à bon escient, par exemple pour distinguer vos variables jQuery
($variable
) ou avoir une information de portée (
_internalVar
).
Typage faible
function(){
var _i = 10;
typeof _i; // "number"
_i = "dix";
typeof _i; // "string"
}
Portée et Hoisting
var testVar = "Variable globale";
function example() {
alert(testVar); // undefined
var testVar = "Variable locale";
alert(testVar); // "Variable locale"
}
example();
Déclarations
// Déclarations multiples
var maVariable1,
maVariable2 = 15,
maVariable3;
// Déclarations de variables égales
var maVariable1 = maVariable2 = 15;
Référence vs. copie
var a = 3.14; // Déclare et initialise une variable
var b = a; // copie le contenu de a dans b
a = 4; // modifie la valeur de a
alert(b); // la copie n'a pas changé : 3.14
var a = [1,2]; // Initialise une variable avec un tableau
var b = a; // copie la référence dans une nouvelle variable
a[0] = 99; // modifie le tableau selon la référence
alert(b); // affiche le tableau modifié : [99,2]
Opérateur d'évaluation : !!
var vrai = !!(0); // faux
var faux = !!("ftw"); // vrai
Sont faux :
var falsies = [false, 0, "", null, undefined, NaN];
Tout le reste est vrai !
Mais attention, certains faux se valent
(false == "") && (false == 0) && (0 == ""); // true
(null == null) && (null == undefined); // true
Mais les deux groupes ne font pas bon ménage...
(null == false) || (null == 0) || (null == ""); // false
(undefined == false) || (undefined == 0) || (undefined == ""); // false
Enfin NaN
n'est égal à personne, pas même lui-même
(NaN == false) || (NaN == 0) || (NaN == "")
|| (NaN == undefined) || (NaN == null); // false
(NaN == NaN); // false
Par rapport à undefined
et null
if (undefined != monObjet) {
// compare l'égalité de mon objet face à 'null' ou 'undefined'
}
Par rapport à son évaluation booléenne
if (monObjet) {
// On entrera que si monObjet n'est pas considéré comme faux
// donc différent de false, "", undefined, null ou NaN
}
// équivaut à
monObjet && // portion de code
// équivaut à
!!(monObjet) && // portion de code
Nommées
function sheldon() {
alert("Bazinga!");
}
sheldon();
Anonymes
var sheldon = function() {
alert("Bazinga!");
}
sheldon();
Pour le test
function callIt(name) {
try { window[name]() }
catch (e) { console && console.log('Erreur durant l\'appel de '+ name); }
}
Test de la portée des fonctions
callIt("a"); // Ok
callIt("b"); // Erreur durant l'appel de b
callIt("c"); // Erreur durant l'appel de c
function a() {}
var b = function () {};
callIt("b"); // Ok
var c = function d() {};
callIt("c"); // Ok
callIt("d"); // Erreur durant l'appel de d
// En réalité, d n'est défini que durant la création de c
Moralité : attention aux fonctions anonymes !
Object : ensemble de valeurs nommées
var obj = new Object();
obj.x = 1;
obj["y"] = 2;
var obj = { x : 1, y : 3 };
Tableaux : ensemble de valeurs indexées
var arr = new Array(1.2, true, { x:1, y:3 });
var arr = [1.2, true, { x:1, y:3 }];
Parcourir avec for ... in
function consoleContenu(element) {
var result = [];
for( var identifiant in element ) {
result.push(identifiant + ":" + element[identifiant]);
}
console && console.log(result.join(', '));
}
consoleContenu(arr); // 0:1.2, 1:true, 2:[object Object]
consoleContenu(obj); // x:1, y:3
Problème : parfois on arrive après la bataille
var img = document.querySelector('.img-1');
img.addEventListener('load', function() {
// l'image est chargée !
});
On n'obtiendra pas le résultat escompté si on arrive après le chargement de l'image...
On peut essayer de corriger ça avec complete
...
var img = document.querySelector('.img-1');
function loaded() {
// l'image est chargée !
}
if (img.complete) {
loaded();
}
else {
img.addEventListener('load', loaded);
}
... mais on n'a aucun équivalent pour l'erreur, par exemple
Les Promises arrivent via des librairies, en attendant leur plein support. Dans l'idée, ça donne ça :
var promise = new Promise(function(resolve, reject) {
// faire un truc, peut-être asynchrone, puis...
if (/* tout a bien marché */) {
resolve("Ces trucs ont marché !");
} else {
reject(Error("Ça a foiré"));
}
});
promise.then(function(result) {
console.log(result); // "Ces trucs ont marché !"
}, function(err) {
console.log(err); // Error: "Ça a foiré"
});
Cool, non ?
Envie d'en savoir plus ?
Allez lire cet excellent article de Jake Archibald traduit par Christophe Porteneuve
La POO repose sur 5 fondamentaux définis entre 1960 et 1970
On peut utiliser Object
var myHero = {
realName: "Clark Kent",
surname: "Superman"
}
Suivant !
Là, ça ne colle plus... Je peux modifier toutes les infos librement
var myHero = {
realName: "Clark Kent",
surname: "Superman"
}
myHero.realName = "Lex Luthor"; // Mouhahahahaaaa !
Mais on sait comment rendre indisponible une variable "à l'extérieur" : il suffit de la mettre dans une fonction
function Hero(){
var realName = "Clark Kent",
surname = "Superman";
}
var myHero = new Hero();
Les champs sont cachés et on peut se servir de Hero
comme constructeur paramétré si on le souhaite
On peut ajouter un accesseur
function Hero(name, surname){
var realName = name,
surname = surname;
this.getSurname = function(){
return surname
};
}
var myHero = new Hero("Clark Kent", "Superman");
alert(myHero.getSurname()); // "Superman"
alert(myHero.realName); // undefined
A chaud, pas trop possible, car JavaScript n’a pas de types évolués : à part les types primitifs, tout est Object
alert(typeof(myHero)); // object
Par contre si je maintiens la liste des types existants, je peux tous les tester et arriver à la conclusion que :
alert(myHero instanceof Hero); // « true »
JavaScript n’enregistre, pour un objet, qu’une seule référence vers une « source » structurelle : son prototype.
Pas de polymorphisme donc, mais on peut pratiquer l'héritage (ce qui, pour certaines personnes, est la règle #5 de la POO). Donc finalement, c'est cool :)
En utilisant le prototype, on peut optimiser l'exemple précédent pour ne pas recopier l'accesseur dans toutes les instances et le définir uniquement quand Hero
n'est pas encore initialisé :
function Hero(name, surname){
var realName = name,
surname = surname;
if (typeof Hero.initialized == "undefined"){
Hero.prototype.getSurname = function(){
return surname
};
}
}
var myHero = new Hero("Clark Kent", "Superman");
alert(myHero.getSurname()); // "Superman"
alert(myHero.realName); // undefined
On peut même réorganiser le code autour des notions de privé et public :
function Hero(name, surname){
var publics = this,
privates = {};
publics.name = name;
privates.surname = surname;
if (typeof Hero.initialized == "undefined"){
Hero.prototype.getSurname = function(){
return privates.surname
};
}
}
var myHero = new Hero("Clark Kent", "Superman");
alert(myHero.getSurname()); // "Superman"
alert(myHero.realName); // undefined
Et ajouter la notion de constructeur, de méthodes statiques et de chaînage des propriétés
/*** Déclaration du type ***/
function Hero(name, surname) {
this.init(name, surname);
Hero.total = typeof Hero.total != 'undefined' ? Hero.total + 1 : 1
}
/*** Enrichissement ***/
(function () {
Hero.total = 0;
/*** Constructeur ***/
Hero.prototype.init = function (name, surname) {
this.__privates = this.__privates || { surname: surname };
name && this.name(name)
};
/*** Getter/Setter ***/
Hero.prototype.name = function (value) {
var privates = this.__privates;
if (typeof value == 'undefined') {
return privates.name
} else {
privates.name = new String(value).toString();
return this
}
};
/*** Getter ***/
Hero.prototype.surname = function () {
var privates = this.__privates;
return privates.surname
}
}());
// Utilisation
var Super = new Hero('Clark Kent', 'Superman'),
Spidey = new Hero('Peter Parker', 'Spiderman');
// Variable statique
alert(Hero.total);
// Chaînage
alert(Spidey.name('Otto Octavius').surname());
alert(Spidey.name())
Mais attention alors à la dépendance au prototype
var myHero = new Hero("Clark Kent", "Superman");
alert(myHero.surname()); // "Superman"
Hero.prototype.surname = function(){ return "Superslip" };
alert(myHero.surname()); // "Superslip"
this
représente le contexte dans lequel s’exécute une fonction au moment où elle est appelée
function quiSuisJe() {
alert(this);
}
var coucou = {
quiSuisJe : function () {
alert(this);
}
};
quiSuisJe(); // [object Window]
coucou.quiSuisJe(); // [object Object]
Les gestion des erreurs est possible en JavaScript
try {
// code JavaScript à surveiller
}
catch (erreur) {
alert(erreur.name);
alert(erreur.message);
}
finally {
// code JavaScript à exécuter dans
// tous les cas (facultatif)
}
Malheureusement, cela entraine une surcharge mémoire indédiable sur V8, aujourd'hui moteur leader.
Maximum, minimum
var x = 9007199254740992; // Max
var y = -x;
x == x + 1; // true
y == y - 1; // true
z = x + 2; // 9007199254740994, ok
z - 1; // 9007199254740992, WTF ?
Utilisez vos opérateurs arithmétiques avec en tête le 64bits mais vos décalages et opérations bit à bit en 32 bits seulement (car vous ne savez pas où est la virgule flottante)
var x = 9007199254740992; // 2^53, [10000000....00]
x / 2; // 4503599627370496, ok
x >> 1; // 0 au lieu de 2^52
x | 1; // 1 au lieu de 2^52 + 1
Pour en savoir plus sur les nombres, foncez-ici.
NaN
est une valeur invalide de la notation à virgule flottante IEEE 754 qui n'est donc égale à rien, même pas à elle-même.
Math.sqrt(-2) != Math.log(-1) // true
Math.sqrt(-2) != Math.sqrt(-2) // true
isNaN()
vous permettra cependant de tester un NaN
, mais elle est imparfaite. Préférez-lui Number.isNaN()
quand c'est possible.
isNaN(NaN) === Number.isNaN(NaN) === true; // Hum, ok !
Number.isNaN('NaN is not a number'); // false, ok
isNaN('NaN is not a number'); // true... WTF ?!
var objects = [{}, {}, {}];
for(var i = 0; i < objects.length; i++) {
objects[i].logIndex = function() {
console.log(i);
}
}
objects[0].logIndex(); // 3, WTF !
objects[1].logIndex(); // 3, WTF !
objects[2].logIndex(); // 3, WTF !
Quand i
est passé au console.log()
, c'est par référence... Pour corriger, voyons d'abord deux astuces !
Test réalisé à chaque fois
function TestURL(){
if (window.location == "http://google.fr"){
alert("Google !");
} else {
alert("Pas Google");
}
}
Test réalisé une fois
function TestURL(){
if (window.location == "http://google.fr"){
return function() {
alert("Google !");
}
} else {
return function() {
alert("Pas Google");
}
}
}
Fonctions anonymes immédiatement appelées, un pattern de développement courant en JS. Permet d'éviter le Hoisting et de créer des portées lexicales (namespaces).
(function(){
// du code
})();
Autre intérêt des IIFE, s'assurer que certain opérateurs sont surchargés comme vous le souhaitez. Exemple, si vous n'êtes pas sûr que $
correspond à jQuery, encadrez votre code de :
(function($){
// votre code
})(jQuery);
Utilisation du renvoi de fonction et d'une IIFE
var objects = [{}, {}, {}];
for(var i = 0; i < objects.length; i++) {
(function(index){
objects[i].logIndex = function() {
console.log(index);
}
})(i)
}
objects[0].logIndex(); // 0!
objects[1].logIndex(); // 1!
objects[2].logIndex(); // 2!
Quand index
est passé au console.log()
, c'est par valeur cette fois !
JavaScript et DOM n'ont pas le même Garbage Collector.
Bien que tous deux capables de gérer leurs références circulaires, ils ne sont pas capables de gérer les dépendances de l'autre univers...
var jsObject;
function FuiteMemoire(idDiv){
// On stocke dans une variable JS une référence au DOM
jsObject = document.getElementById(idDiv);
/* Plein de code */
// Et là, le développeur se rate et référence l’objet JS
// depuis le DOM
document.getElementById(idDiv).expandoProperty= jsObject;
}
// Epic Fail : Memory Leak
Variante : le développeur référence depuis l'intérieur d'une énorme méthode un élément du DOM, qui lui même référence l'objet.
function Encapsule(element){
// Assignation d’une propriété
this.elementReference = element;
// Référence circulaire
element.expandoProperty = this;
}
function EnormeMethode() {
/* Plein de code */
Encapsule(document.getElementById("maDiv"))
/* Encore plein de code */
}
EnormeMethode
ne pourra jamais être nettoyée.
Variante la plus courante : attachement d'un évènement à une méthode anonyme, empêchant le GC de nettoyer l'un ou l'autre si l'un des éléments est supprimé.
window.addEventListener("load", function(){
// obj sera ramassé par le GC dès qu’il sera sorti du contexte
var obj = document.getElementById("element");
// en revanche, la liaison ici relie un objet DOM à une
// méthode anonyme du JS, définie dans le même contexte que obj
obj.addEventListener("click", function(evt){
// logique
}, false);
}, false);
Présent dans 99% des scripts.
Pour éviter ces problèmes, utilisez removeEventListener
ou off
(jQuery).
var item = document.createElement("div");
var listener = function(event) {
// du code
// on enlève le listener pour éviter la fuite
this.removeEventListener("click", listener, false);
};
item.addEventListener("click", listener, false);
(liste non-exhaustive)
Framework rapide, compact et gérant très bien l'asynchrone de John Resig, ayant remporté un grand succès, principalement parce qu'il a été adopté par les intégrateurs HTML.
Souvent utilisé pour faire des choses simples. Au lieu de les faire simplement.
// jQuery
$(el).hide();
$(el).show(); // Ajoute un "display:block", WTF ?!
// IE8+
el.style.display = 'none';
el.style.display = ''; // Ne fait que ce que vous lui demandez
Jetez un oeil à MicroJS et ses micro-librairies (inférieures à 5Ko) rendant chacune un service très précis.
MicroJS est sur GitHub et n'importe qui peut y ajouter son script licencié MIT ou équivalent.
Exemple : preloadr.js
“JSLint can suck it”, Brendan Eich
@
pour this
et ::
pour les méthodes de prototype.
var d = new Date(Date.UTC(2014, 10, 26, 13, 31, 56));
// Wed Nov 26 2014 14:31:56 GMT+0100 (Paris, Madrid)
d.getYear() // => 114
d.getMonth() // => 10
d.getDay() // => 3
var d = new Date(1845, 3, 17);
// Thu Apr 17 1845 00:00:00 GMT+0200 (Paris, Madrid (heure d’été))
var d2 = new Date(2014, -2163, 4217); // Gni ?
// Thu Apr 17 1845 00:00:00 GMT+0200 (Paris, Madrid (heure d’été))
result = [] + [];
result = [] + []; // ""
result = {} + [];
result = {} + []; // [object Object]
result = '4' - '2';
result = '4' + 2;
result = '4' + + '2';
result = '4' + - '2';
result = '4' + - + - - - + + + + - + - - '-2';
result = '4' - '2'; // 2
result = '4' + 2; // "42"
result = '4' + + '2'; // "42"
result = '4' + - '2'; // "4-2"
result = '4' + - + - - - + + + + - + - - '-2'; // "42"
var x = 42;
result1 = '42' - x + x;
result2 = '42' + x - x;
var x = 42;
result1 = '42' - x + x; // 42
result2 = '42' + x - x; // 4200
result = 'ro' + + 'ro' + ' the accuser';
result = 'ro' + + 'ro' + ' the accuser'; // "roNaN the accuser"
"wtf" - 1
"wtf" - 1 // NaN
result = Array(16).join("wtf" - 1) + ' Batman !';
result = Array(16).join("wtf" - 1) + ' Batman !';
// "NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN Batman !"