Le côté objet de javascript

Publié le 17 mai 2011 par Martin Catty | front

Cet article est publié sous licence CC BY-NC-SA

Javascript est un langage objet, même s’il ne possède pas un modèle de programmation par classes mais une modèle orienté prototype.

Un objet n’est donc pas une instance de classe mais un clone de prototype, et peut être modifié «à chaud».

Ce concept est extrêmement puissant [1], puisque l’héritage est dynamique, c’est à dire qu’un objet peut changer de parent en cours de route (cf. section Héritage)

Le fait d’ajouter une fonction dans le prototype, rend cette fonction disponible immédiatement dans l’ensemble des clones.

var a1 = ["4", "2"]
Array.prototype.size = function() { return this.length; }
var a2 = ["s", "y", "n", "b", "i", "o", "z"]
a1.size()
=> 2
a2.size()
=> 7

Javascript, un langage tout objet

Javascript est un langage interprété et typé dynamiquement. Il existe deux moyens de connaitre le type d’un objet:

var a = true
typeof a
=> "boolean"
a.constructor.name
=> "Boolean"

typeof Boolean
=> "function"
Boolean.constructor.name
=> "Function"

typeof Function
=> "function"

Function représente donc la base de l’héritage.

Visibilité et portée

Il est possible au sein d’une fonction de définir des variables ou méthodes qui ne seront pas accessibles de l’extérieur.

function A() {
  // private
  var _foo = "foo";

  function foo() {
    return _foo;
  };

  // public
  this._bar = "bar";
  this.bar = function() {
    return this._bar;
  }

  // public method returning private attribute
  this.baz = function() {
    return _foo;
  }
}

var a = new A;
a.foo()
=> TypeError: a.foo is not a function
a._foo
=> "undefined"
a.bar()
=> "bar"
a._bar
=> "bar"
a.baz()
=> "foo"

// static method
A.parse = function() {  }

var a = new A
a.parse()
// TypeError: a.parse is not a function

Simuler la notion de packages

La notion de namespace n’existe pas à proprement parler en Javascript mais elle est très simple à simuler:

var com = {};
com.synbioz = {};

com.synbioz.app = function(name, url) {
  this._name = name;
  this._url = url;
}

new com.synbioz.app("foo", "foo.com")

Héritage

Il faut imaginer la méthode prototype comme un pointeur vers le constructeur dans un héritage par classe.

function Human() { this.speak = function() { return "bonjour" } }
function Monster() { this.speak = function() { return "grrrr" } }

function Hulk() {}

// hulk inherits from Human
Hulk.prototype = new Human()
hulk1 = new Hulk()
hulk1.speak()
=> "bonjour"

// hulk inherits from Monster
Hulk.prototype = new Monster()
hulk2 = new Hulk()
hulk2.speak()
=> "grrrr"

// warning:
hulk1.speak()
=> "bonjour"

Le changement de constructeur n’impacte pas les objets déjà créés car la propriété prototype de hulk1 n’a pas été modifiée, c’est le prototype du parent (Hulk) qui a été modifié.

// still does'nt work
hulk1.prototype = new Monster();
hulk1.speak()
=> "bonjour"

Ce code n’a logiquement aucun effet, on ne peut pas modifier directement le prototype de l’objet, cela reviendrait à vouloir modifier le constructeur d’une instance dans un héritage par classe.

Toutefois, dans certaines implémentations (ex: mozilla), l’objet garde une référence vers le prototype parent.

Attention, la propriété n’est pas standard et ne fonctionne pas sur tous les navigateurs.

hulk1.__proto__ = new Monster
hulk1.speak()
=> "grrrr"

[1]: Attention, Un grand pouvoir implique de grandes responsabilités.

L’équipe Synbioz.

Libres d’être ensemble.