Le langage Javascript

Ou le langage du web

Par Vivian Pennel / @vp3n
Lead Developer chez IOcean

Un peu d'histoire

1995 - 1997 : Création

  • Création en 1995 par Brendan Eich de LiveScript
  • Marketing: LiveScript devient Javascript
  • 1997: standardisation amorcée : spécification ECMAScript
  • Initialement supporté uniquement par Netscape

1997 - 2000 : Concurrence

  • Microsoft créé sa propre implémentation : JScript
  • Flash fait de même avec ActionScript
  • Javascript était donc voué à comporter des problèmes de compatibilité

2000 : Adoption et dénigrement

  • Javascript et la révolution des pages web dynamiques
  • Langage dénigré par son "amateurisme"

2000 - 2005 : Ajax

  • Invention par Microsoft de XMLHTTP
  • Portage sur les autres navigateurs : XMLHttpRequest
  • 2004: Gmail
  • 2005: Google Maps
  • 2005 : Invention du terme AJAX par Jesse James Garret
  • 2006: Standardisation d'AJAX par le W3C

2005 - 2008 : Première explosion

  • AJAX a permis à Javascript une adoption fulgurante
  • Prototype, jQuery, Dojo et Mootools
  • Développements orientés DOM

2009 - De nos jours : Seconde explosion

  • Création de NodeJS
  • HTML5
  • ECMAScript 5
  • Industrialisation
  • Javascript everywhere

Paradigme

Paradigme

  • Melting pot de plusieurs paradigmes
  • Impératif et structuré
  • Orienté objet (par prototype)
  • Fonctionnel (first class functions)
  • Typage dynamique et faible

Les types de données

Primitives

  • Number
  • String
  • Boolean
  • null
  • undefined
  • Les types primitifs sont immuables

Autres types

  • Tout ce qui n'est pas un type primitif est un objet
  • Date
  • RegExp
  • ....

Cas particuliers

  • Function est un type objet particulier
  • Array

Les prototypes

Les prototypes

  • Opposition au modèle de classe
  • Définition au runtime au lieu de statique
  • La composition des objets, leur liens et héritage peut donc être modifié à souhait
  • Un prototype est un objet dont d'autres objets peuvent hériter de ses propriétés

Les prototypes

  • Chaque objet en javascript a un prototype
  • Initialisation implicite de "this" en fonction du contexte
  • Tout appel à une propriété remonte la chaine de prototype jusqu'à trouver (ou pas) une valeur
  • Les fonctions elles-même ont un prototype qui hérite d'object

Exemples : prototype simple


function Person(firstName) {
    this.firstName = firstName;
}
Person.prototype.getFirstName = function() {
    return this.firstName();
}
//p a maintenant le prototype Person
// et une propriété constructor affectée à Person()
var p = new Person();
                

Exemples : héritage


function Employee(isSenior) {
    this.isSenior = isSenior;
}
Employee.prototype.isSenior = function() {
    return this.isSenior;
}
Employee.prototype = Person.prototype;
Employee.prototype.constuctor = Employee;
                

L'opérateur instanceof

  • Retourne vrai si un des prototypes de la chaine correspond
  • 
    var A = function() {}
    var a = new A();
    console.log(a instanceof A); //true;
    console.log(a instanceof Object); //true;
                        

ECMAScript 5 : Object.create()


// definit aucun prototype
var animal = Object.create(null);
console.log(animal.prototype); //undefined
animal.color = 'black';

var cat = Object.create(animal);
// Object {color: "black"}
console.log(Object.getPrototypeOf(cat));
cat.age = 12;
//Object {age: 12, color: "black"}
console.log(cat);

var a = Object.create(Object.prototype) <=> var a = {}

                

Ressources sur les prototypes

Le scope des variables

Scope

  • Pas de scope "bloc" en javascript
  • Le scope d'une variable est la fonction
  • Une variable existe si elle est définie n'importe où dans la fonction
  • Pour éviter les erreurs, la bonne pratique est de déclarer toutes les variables en début de fonction

Déclaration et variable globale

  • Toutes les variables doivent être déclarées avec le mot clef "var" sinon elles sont globales
  • En mode normal, si this n'est pas défini il est égal à window (dans un contexte navigateur)
  • Dans un navigateur toute variable définie à l'extérieur d'une fonction (même avec var) est globale
  • Toujours utiliser le mode 'use strict'; qui force a la bonne déclaration entre autre chose

Les 4 manières d'appeller une fonction

Simple sans contexte : appel de fonction


//wrapper de fonction, évite de déclarer des variables globales !
// le scope est réduit à la fonction anonyme
(function() {
    'use strict';

    function myFunction() {
        // undefined, sans 'use strict' ce serait égal à window !
        alert(this);
    }

    myFunction();

})(); // exécute la fonction immédiatement
                

Contexte d'un objet : appel de méthode


(function() {
    'use strict';

    var myObject = {
        myMethod : function() {
            // object - correspond à l'objet myObject
            alert(this);
        }
    }

    myObject.myMethod();

})();
                

Avec new : appel d'un constructeur


(function() {
    'use strict';

    // object - correspond à une instance de MyConstructor
    // La majuscule en début est très importante!
    // Seule une convention différencie l'appel
    // de constructeur d'une fonction
    var MyConstructor = function() {
        alert(this);
    }

    var myObject = new MyConstructor();
})();
                

Attention : appel d'un constructeur


(function() {
    'use strict';

    // La majuscule permet d'identifier
    // qu'il s'agit d'un constructeur
    // et donc que l'on doit utiliser "new"
    var MyConstructor = function() {
        alert(this); // undefined !
    }

    // rien ne nous empêche d'appeller le constructeur
    // en mode fonction
    var myObject = MyConstructor();
})();
                

Avec call() ou apply() : contextualiser l'appel


(function() {
    'use strict';

    function myFunction(arg1, arg2) {
        alert(this.myProp); // 1
    }

    var arg1, arg2;
    // On choisit la valeur de this
    // principe très utilisé dans des lib comme jQuery
    myFunction.call({myProp: 1}, arg1, arg2);
    myFunction.apply({myProp: 1}, [arg1, arg2]);

})();
                

Les manières de créer des objets

Les objets littéraux


(function() {
    'use strict';

    // déclaration directe
    // pattern "singleton" gratuitement
    var myObject = {
        myProperty : 2,
        myMethod: function() {
            alert(this.myProperty); // 2
        }
    }

    myObject.myMethod();

})();
                

En mode constructeur


(function() {
    'use strict';
    // La majuscule permet d'identifier
    // qu'il s'agit d'un constructeur
    // et donc que l'on doit utiliser "new"
    var MyConstructor = function(value) {
        this.myProperty = value;
        alert(this);
    }
    MyConstructor.prototype.myMethod() {
        alert(this.myProperty);
    }
    var myObject = new MyConstructor(1); myObject.myMethod(); // 1
    myObject = new MyConstructor(1); myObject.myMethod(); // 2
})();
                

Avec des closures


(function() {
    'use strict';
    function myObject(myParam) {
        var myPrivateProperty = myParam + 1;
        function myPrivateFunction() {
            alert(myPrivateProperty);
        }
        return {
            myPublicProperty : 'abc',
            myPublicMethod : function() {
                return myPrivateFunction();
            }
        }
    }
    var instance = myObject(1);
    instance.myPublicMethod(); //2
})();
                

Les grands pièges

Variables globales

  • Toujours utiliser le mode "use strict"
  • Toujours utiliser "var"
  • Attention au scope function

This et ses contextes

  • Sans 'use strict' dans un navigateur : this = window = global
  • avec use strict, sans "new" this = undefined
  • avec call() ou apply() this = ce qu'on souhaite
  • Figer le contexte : myFunction.bind(context)

Exemple classique d'erreur avec this


function Person() {
    jQuery("selecteur").click(function(e) {
        this.myFunction(); //error myFunction is not defined
    });
}

Person.prototype.myFunction = function() {
    return 'myValue';
}

var a = new Person();
                

Exemple classique d'erreur avec this


function Person() {
    var self = this; // ou that = this
    jQuery("selecteur").click(function(e) {
        self.myFunction(); //myValue
    });
}

Person.prototype.myFunction = function() {
    return 'myValue';
}

var a = new Person();
                

Eval() ca rime avec mal

  • Eval() interprète du code à la volée
  • Ne jamais utiliser sans une très bonne raison
  • Nid à bug
  • Peut rendre impossible la minification du code
  • Impact sur les performances

Eval() exemple


(function() {

    'use strict';

    // que va t-il se passer après minification?
    var myFunction = function() {
        var property;
        eval('property = 3');
        alert(property); // affiche 3
    }

    myFunction();
})();
                

Boucle for..in et prototype

  • Boucle sur toutes les propriétés du prototype (et pas seulement celles de l'objet courant !)
  • Nécessite un test avec hasOwnProperty() pour filtrer
  • ~10 à 20 fois moins performant qu'un for classique
  • A utiliser avec parcimonie

for..in exemple


function() {

    'use strict';

    var arr = ['a','b','c'], indexes = [];
    Array.prototype.myProperty = 'myValue';

    for (var index in arr) {
        indexes.push(index);
    }

    //["0", "1", "2", "myProperty", myProperty: "myValue"]
    console.log(indexes);
})();
                

Closures et boucle


function() {
  'use strict';

  var i;
  for(i = 0; i < 10; i++) {
    setTimeout(function() {
    //qu est-ce qui va être affiché ?
      console.log(i);
    }, 1000);
  }

})();

Closures et boucle - solution


function() {
    'use strict';

    var i;
    for(i = 0; i < 10; i++) {
        (function() { // nouveau scope
            var j = i; //capture de la valeur
            setTimeout(function() {
                console.log(j);
            }, 1000);
        })();
    }
    // affiche de 0 à 9
})();

Typage et opérateurs de comparaison

  • == et != compare uniquement la valeur en effectuant des convertions de type implicites
  • Toujours préférer === et !== qui compare également le type

Typage et opérateurs de comparaison


(function() {

    'use strict';

    console.log(1 == true); //true
    console.log(1 === true); //false

    console.log(1 == '1'); // true
    console.log(1 === '1'); // false
})();
                

L'opérateur "delete"

  • Sur la propriété d'un objet met la propriété à undefined - ok
  • Idem sur un array (array est un objet particulier) - warning

Exemple delete()


var a = ['a', 'b', 'c', 'd'];

console.log(a); //["a", "b", "c", "d"]
delete(a[2]);
console.log(a); //!! ["a", "b", undefined, "d"]
                

la valeur NaN

  • Ne jamais comparer à NaN
  • Utiliser isNaN()

Exemple NaN


var a = parseInt('A');
console.log(a); // NaN
console.log(a == NaN); // false
console.log(a === NaN); //false
console.log(NaN !== NaN); // true

console.log(isNaN(a)); // true - Bonne réponse !
                

Chercher des infos sur internet - WARNING

  • Beacoup d'informations dépassées, de piètre qualité ou carrément mauvaise
  • Si possible chercher dans la documentation de Mozilla
  • cheat sheet avec liens vers Mozilla
  • Bon récapitulatif : Javascript Garden
  • Eviter w3school !