# Un linguaggio classless #
JavaScript e' un linguaggio che non ha classi ma bensi' funzioni costruttore. Ormai lo sannno anche i muri ;-) Questo significa che non esiste il costrutto class e che tutta una serie di logiche di programmazione "classiche" o "Object Oriented" nel senso stretto del termine decadono e non sono valide all'interno di questo linguaggio.
# Funzioni costruttore #
Ogni funzione e' in se stessa un costruttore e se viene eseguita come operando dell'operatore unario new la funzione ritorna un oggetto nuovo nuovo che possiamo salvare in una varibile. Ecco come:
codice:
function Foo(){}
var o = new Foo();
Ogni funzione eseguita come operando di new ritorna this. Chi e' this? E' l'oggetto che viene creato da new e che Foo ha la possibilita' di personalizzare prima di ritornarlo alla variabile o. Personalizzare? Si! Infatti se aggiungiamo al corpo della funzione Foo del codice di questo tipo:
codice:
this.nomeAttributo = "value";
this.nomeMetodo = function(){
// do stuff!
}
nomeAttributo e nomeMetodo vengono aggiunti all'istanza e il nostro o e' ora un po' piu' interessante e potenzialmente utile nel raggiungimento dei nostri sporchi obiettivi.
Nel 90% dei casi un programmatore JavaScript lavora a stretto contatto con i propri costruttori e non bada molto a Object(), Function(), Array(), String(), Number() ecc. ecc. Questi costruttori nativi sono spesso fonte di confusione (lo sono stati per me almeno ;-) e la non completa chiarezza relativa allo status logico di questi elementi porta all'offuscamento (parziale o totale) dei meccanismi di funzionamento del linguaggio lassu' nelle alte sfere della gerarchia dei prototipi [tutto sara' piu' chiaro fra un centinaio di righe!].
# Costruttori nativi #
Prima di tutto diamo un occhiata (per quanto possibile) a questi costruttori. E' molto illustrativo (e divertente) vedere cosa accade quando si esegue un'alert di questi costruttori nativi. Generalmente quando si allerta il nome di una funzione in JS si ottiene il codice della stessa (). Cosa accade invece se eseguiamo un alert su Object() o Array()? Ecco il codice:
codice:
alert(Array) // provare per credere!
Divertente no? Il costruttore e' in tutto e per tutto una funzione che istanzia un array (se usato assieme a new). Cosa la funzione faccia non ci e' dato sapere (vadi [native code] nell'alert). Possiamo pero' fare delle deduzioni sulla base degli oggetti che istanziamo usando questo costruttore. Per esempio il costruttore Array creera', fra le altre cose, una proprieta' di instanza chiamata length contenente il numero di elementi del nuovo array creato. In pratica eseguira' qualche cosa del genere:
codice:
var Array = function(n){
if(arguments.length == 1 && typeof arguments[0] == "number"){
this.length = n;
}
else {
this.length = arguments.length;
}
// other code here ...
}
Ognuno di questi costruttori nativi possiede poi un prototype da cui le istanze "ereditano". Per esempio il prototipo di Array e' nativamente dotato di un numero di metodi che tutti gli array possono adoperare. Infatti abbiamo che:
codice:
Array.prototype.hasOwnProperty("sort") // true
Array.prototype.hasOwnProperty("pop") // true
Array.prototype.hasOwnProperty("slice") // true
// ecc..
Detto cio' cerchiamo di capire che senso abbiano questi costruttori e in che relazione stiano con i costruttori che scriviamo "a mano". La comprensione dell'intero ambaradan passa per alcune osservazioni chiave. Infatti abbiamo che:
codice:
Object instanceof Object == true // true
Object instanceof Function == true // true
Function instanceof Object == true // true
Function instanceof Function == true // true
Array instanceof Object == true // true
Array instanceof Function == true // true
// tralascio gli altri costruttori nativi (String(), Boolen() ecc.) per brevita'
Inoltre in riferimento ai vari prototype dei construttori nativi si ha che:
codice:
Object.prototype instanceof Object == true // false - siamo all'apice della catena!
Function.prototype instanceof Object == true // true
Array.prototype instanceof Object == true // true
Ho cercato di descrivere lo stato di cose appena rilevato con il seguente grafico. Da esso si puo' dedurre abbastanza intuitivamente chi eredita da cosa e quale sia la gerarchia dei prototipi che l'engine adopera durante la compilazione del codice.
# LETTERALI #
Va in oltre oservato che in JS non e' necessario (e' anzi stilisticamente sconsigliato) usare esplicitamente questi costruttori. Infatti invece di:
codice:
var o1 = new Object();
var o2 = new Array("1","2","3");
var o3 = new String("Nourdine")
si possono utilizzare le seguenti notazioni:
codice:
var o1 = {};
var o2 = ["1","2","3"];
var o3 = "Nourdine";
Che sono sostanzialemte identiche e presuppongono l'uso implicito dei cotruttori nativi relativi. Quindi, in pratica, ognuno di noi adopera (implicitamente) molto spesso questi costruttori nativi senza rendersene conto. Al di la di questo fatto banale, e tenendo conto della gerarchia di prototipi messa in evidenza nel grafico di cui sopra, si puo' mettere in luce il seguente interessante uso dei costruttori nativi.
# Argumentation #
L'agomentazione dei tipi fondamentali e' un po' la chiave del senso recondito dei costruttori nativi. In effetti la cascata di ereditarieta' messa in luce nel grafico suggerisce l'implementzione di alcune utili estensioni atte a coprire deficienze del linguaggio. Per argomentare un costruttore (costruttori nativi inclusi) si usa di solito del codice simile a questo:
codice:
Function.prototype.method = function(name, fun){
this.prototype[name] = fun;
return this
}
Ora di da il caso che String instaceof Function == true (vedi grafico). Questo fatto permette di usare method sul costruttore nativo String (come pure su ogni altra funzione) per aggiungere a cascata funzionalita' a tutti gli oggetti istanziati usando questo costruttore (o la relativa notazione letterale!): in pratica tutte le stringhe del mondo! Ecco per esempio un metodo che permette di fare il trim delle stringhe (funzionalita' non presente in JS core). Ecco come:
codice:
String.method("trim", function(){
var pattern = /^\s*|\s*$/g;
return this.replace(pattern, "");
});
var str = " ciao ";
document.write(str.trim()); // stampa "ciao" senza spazi bianchi. cooooool!
Altre estensioni del linguaggio sono possibili. Dipende tutto dalle vostre necessita' e dal progetto su cui stato lavorando. Spero di essere stato utile ... se ho scritto minchionate commentate senza remore!
"winning some, losing some" [A. Difranco]
Nourdine