Come costruire ed ereditare oggetti in Javascript sfruttando i prototipi

In generale, il metodo più rapido per definire oggetti in Javascript è inserire proprietà e metodi direttamente nel costruttore.

Ad esempio, la seguente funzione A() definisce due proprietà e due metodi:

function A() { //costruttore
   this.p1 = 0; //proprietà 1
   this.p2 = ''; //proprietà 2
   this.m1 = function() {
      //metodo 1
   };
   this.m2 = function() {
      //metodo 2
   };
}

La sintassi per creare ed utilizzare l’oggetto definito sopra è la seguente:

var a = new A(); //creazione dell'oggetto
a.p1 = 99; //assegnazione della proprietà p1
a.m2(); //chiamata al metodo m2()

Un approccio alternativo è quello di utilizzare i prototipi per definire le proprietà ed i metodi degli oggetti. Di seguito sono elencate tre varianti di codice Javascript per definire lo stesso oggetto di prima, da preferite a seconda del contesto (e magari anche dei gusti personali…):

Variante 1
Parte dell’oggetto viene definita nel costruttore, e parte nel prototipo

function A() {
   this.p1 = 0;
   this.p2 = '';
}
A.prototype = {
   m1: function() {
      //...
   }
   ,m2: function() {
      //...
   }
};

Variante 2
Proprietà e metodi vengono definiti nel prototipo

function A() {
}
A.prototype = {
    p1: 0
   ,p2: ''
   ,m1: function() {
      //...
   }
   ,m2: function() {
      //...
   }
}

Variante 3
Come la variante 2, ma i metodi vengono definiti con una sintassi differente

function A() {
}
A.prototype = {
    p1: 0
   ,p2: ''
};
A.prototype.m1 = function() {
      //...
};
A.prototype.m2 = function() {
      //...
};

Eredità degli oggetti in Javascript
Vediamo infine un esempio per definire un oggetto derivato dal precedente, sempre utilizzando i prototipi.
Attenzione che, usando la variante 1, tutto ciò che è definito all’interno del costruttore (nell’esempio sopra le proprietà p1 e p2 ) non viene ereditato perché non appartiene al prototipo.

function B()
{
    this.__proto__.__proto__ = A.prototype; //eredita il prototipo da A
}
B.prototype = {
    _p3: '' //valore di default di p3
    ,get p3()
    {
        return 'p3 is ' + this._p3;
    }
    ,set p3(value)
    {
        this._p3 = value.toLowerCase();
    }
}

Nel prototipo di B sono state aggiunte le keyword get e set per definire le funzioni che vengono chiamate quando si assegna o si legge il valore della proprietà p3.

Sintassi:

var b = new B();
b.m1();  // chiamata del metodo m1 definito in A
b.p3 = "PIPPO";
alert(b.p3); // output: p3 is pippo

Nota: questi esempi sono stati verificati in Droidscript.