Classes et héritage en ES6 et TypeScript
Avec l’ES6, une nouvelle façon d’écrire les classes a fait son apparition au sein de JavaScript, cet article en décrit les principaux fonctionnement:
Déclaration de classe, constructeur, hosting, méthodes, héritage, setter / getter, encapsulation, méthodes statiques…
Déclarer une classe
Une classe en ES6, se déclare avec le mot class.
On peut l’écrire de la façon suivante :
class Rectangle { constructor(height width){ this.height = height this.width = width } }
ou de manière anonyme.
'use strict' var Rectangle = class { constructor(height, width){ this.height = height this.width = width } }
Hoisting
Les déclarations de classe sont remontées dans le code, en revanche, une déclaration de classe n’est pas remontée dans le code.
var rec = new Rectangle () // ReferenceError class Rectangle {}
Constructeur
Le constructeur est appelé au moment de l’instanciation de la classe.
Il doit être nommé constructor. Une seule méthode doit être nommé constructor dans une classe, sinon une exception sera levée SyntaxError.
On peut appeler le constructeur parent lors d’un héritage, en utilisant le mot clé super.
Définition de méthode
Une méthode se déclare de la sorte :
nomDeLaMethode (){ // contenu de la méthode }
On peut donc les écrire ainsi :
'use strict' class Rectangle { constructor(height, width){ this.height = height this.width = width } getArea (){ return this.height * this.width } setHeight(height){ this.height = height } setWidth(width){ this.width = width } } const rec = new Rectangle(100, 50) console.log(rec.getArea()) // retourne 5000 rec.setHeight(3) console.log(rec.getArea()) // retourne 150 rec.setWidth(39) console.log(rec.getArea()) // retourne 117
Méthode statique
Une méthode statique s’écrit avec le mot clé static devant la méthode.
Syntaxe
static nomDeLaMethode (){ // contenu de la méthode }
La méthode est appelé sans instancier la classe et ne peuvent d’ailleurs pas être appelé depuis une instance.
'use strict' class Mathematics { static getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } } cont uid = Mathematics.getRandomInt(2, 6) console.log(uid) // retourne par exemple 3
On ne peut pas utiliser le mot this dans une méthode statique.
class Personn { constructor (){ this.name = 'joe' } static getNameStatic() { return this.name } getNameInstance(){ return this.name } } var joe = new Personn() console.log(joe.getNameInstance()) // retourne 'joe' console.log(Personn.getNameStatic()) // Retourne Personn
Héritage
Pour étendre une classe, il faut utiliser le mot clé extends.
class Human { constructor (){ this.name = 'anonyme' this.sex } getName (){ return this.name } getSex (){ return this.sex } } class Woman extends Human { constructor(){ super() this.sex = 'Femme' } setAge(age){ this.age = age } getAge(){ return this.age + ' ans' } } let w = new Woman() w.name = 'Charlie' w.setAge(23) console.log(w.getName()) console.log(w.getSex()) console.log(w.getAge())
Mais on peut mélanger des classes ES5 avec des classes en ES6.
function Human (){ this.name = 'anonyme' this.sex } Human.prototype.getName = function (){ return this.name } Human.prototype.getSex = function (){ return this.sex } class Woman extends Human { constructor(){ super() this.sex = 'Femme' } setAge(age){ this.age = age } getAge(){ return this.age + ' ans' } } let w = new Woman() w.name = 'Charline' w.setAge(23) console.log(w.getName()) console.log(w.getSex()) console.log(w.getAge())
On peut appeler une méthode parent au sein d’une méthode appartenant à une classe fille.
class Humain { parler(){ console.log("L'humain parle dans la classe mère !") } } class Homme extends Humain { parler(){ super.parler() console.log("L'homme parle dans la classe fille !") } } var h = new Homme () h.parler()
Setter / Getter
En ES6, on peut utiliser des getter et setter.
class User { constructor(name, type){ this.name = name this.type = type } sayName(){ return `Nom : ${this.name}` } get role (){ return this.type } set role (value){ return this.type = value } } const user = new User("jack", "admin") console.log(user.role) user.role = "testeur" console.log(user.role)
En TypeScript, cela ne change pas beaucoup
class Hero { private _name: string set name(name: string){ this._name = name } get name(): string { return this._name } } let e = new Hero() e.name = 'Julien Boisvert' console.log(e.name)
Encapsulation
Pour pouvoir avoir une propriété protégée, nous allons utiliser l’object WeakMap qui représente une collection de paires clé/valeur dont les clés sont des objets.
const first_name = new WeakMap() class User { constructor (firstname){ first_name.set(this, firstname) } get name(){ return first_name.get(this) } } var user1 = new User('Aurélie') console.log(user1.name) class Homme extends User { } var user2 = new User('Gabriel') console.log(user2.name)
C’est sympa mais un peu compliqué pour pas grand chose. Et c’est là que TypeScript pour toute sa force dans la simplicité.
Propriété
En TypeScript, on déclare ses propriétés, non pas dans le constructeur mais dans le corps de la classe.
On défini une propriété privée avec le mot clé private.
// personn.ts class Personn { private name: string private age: number constructor (name: string){ this.name = name } setAge(age: number){ this.age = age } } class Homme extends Personn { constructor(name: string){ super(name) } getAge (){ return this.age } } var user = new Homme('Arthur') user.setAge(23) console.log(user.getAge())
Si on décide de transcompiler le code précédent, on obtient l’erreur :
personn.ts(19,21): error TS2341: Property 'age' is private and only accessible within class 'Personn'.
Si on change le membre private de l’attribut age, en protected, le code fonctionne alors.
class Personn { private name: string protected age: number // ...
Readonly
On a donc des attributs publiques, privés et protégés, mais on peut aller encore plus loin, en ne permettant pas de modifier la valeur d’une propriété.
class User { readonly name: string constructor(name: string){ this.name = name } } let user1 = new User('Avrell') user1.name = 'Jack' // error
Les documentations officielles :
- https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Classes/constructor
- https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Classes/extends
- https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Classes/static
- https://docs.microsoft.com/fr-fr/scripting/javascript/reference/class-statement-javascript
- http://www.typescriptlang.org/docs/handbook/classes.html