JavaScript possiede cinque tipi di dati primitivi:

Undefined
Null
Boolean
Number
String

Un Boolean e' un oggetto che consiste di un valore vero o falso. Un esempio potrebbe essere:

var BooleanValue = true;



Un Number e' un insieme di cifre numeriche che rappresentano un numero. Ad esempio:

var NumericValue = 534;



Una Stringa, infine, e' un insieme di zero o piu' caratteri:

var StringValue = "This is a string!";



Typeof
Se c'e' un operatore poco conosciuto in JavaScript, questo e' sicuramente typeof. Typeof ci dice il tipo di dati con cui stiamo lavorando. Proviamo a vedere un esempio:

var BooleanValue = true;
var NumericalValue = 354;
var StringValue = "This is a String";
alert(typeof BooleanValue) // displays "boolean"
alert(typeof NumericalValue) // displays "number"
alert(typeof StringValue) // displays "string"



Oggetti
Un oggetto e' una collezione di proprieta' (properties). Queste ultime possono essere sia tipi di dati primitivi, sia oggetti e funzioni. In quest'ultimo caso prendono il nome di metodi e li vedremo per esteso piu' in la' in quest'articolo. Una funzione costruttore (o piu' semplicemente, costruttore) e' una funzione usata per creare un oggetto. JavaScript possiede molti oggetti built-in come Array, Image, Date, etc. Molti di voi saranno sicuramente familiari con gli oggetti Image, utili per creare effetti di roll-overing molto efficaci. Ad esempio, quando usiamo il codice:

var Image1 = new Image();
Image1.src = "myDog.gif";



Abbiamo creato un nuovo oggetto Image ed assegnato una property a questo nuovo oggetto: la proprieta' src. Image1 e' un'istanza dell'oggetto Image. Utilizzando la struttura puntata (.) di JavaScript, il codice sopra accede e setta la proprieta' src del nostro nuovo oggetto. Ma come creare le proprie funzioni?

function myFunc(){
}

var myObject = new myFunc();
alert(typeof myObject); // displays "object"



Nel codice sopra abbiamo appena creato un nostro oggetto. myFunc() e' una funzione costruttore. A questo punto, potremmo chiederci come riesce JavaScript a capire quando e come creare un'istanza dell'oggetto myFunc, invece di ritornare il valore della funzione chiamata. Proviamo a confrontare il codice sopra con il seguente:

function myFunc(){
return 5;
}

var myObject = myFunc();
alert(typeof myObject); // displays "number"



In questo caso abbiamo assegnato il valore 5 a myObject. Dove sta la differenza tra questi due codici? La risposta e' la parola chiave: new. New dice a JavaScript di creare un oggetto seguendo i passi esplicitati nel corpo della funzione costruttore myFunc. Quando creiamo l'oggetto Image, ad esempio, facciamo la stessa cosa usando il costruttore built-in di JavaScript per l'oggetto Image, anziche' un nostro costruttore personale.

Adesso sappiamo come creare una funzione costruttore ed un oggetto attraverso il suo costruttore. Nel nostro esempio, abbiamo creato il costruttore myFunc() e creato un'istanza dell'oggetto, assegnandola alla variabile myObject. Tutto carino, certo, ma dove sta il punto? Tanto per cominciare, l'oggetto myObject, come il suo padre Image, puo' avere delle properties:

function myFunc(){
}

var myObject = new myFunc();
myObject.StringValue = "This is a String";
alert(myObject.StringValue); // displays "This is a String"



Nel codice sopra abbiamo creato una property per il nostro oggetto. Purtroppo, l'approccio sopra soffre di un grave problema. Se creiamo una nuova istanza dell'oggetto myFunc, dovremo riassegnare la proprieta' StringValue alla nuova istanza appena creata. Ad esempio:

function myFunc(){
}

var myObject = new myFunc();
myObject.StringValue = "This is a String";
var myObject2 = new myFunc();
alert(myObject2.StringValue); // displays "undefined"



Come possiamo creare delle proprieta' comuni per tutti gli oggetti? La risposta risiede all'interno della funzione costruttore myFunc(). La parola chiave this, all'interno del costruttore, si riferisce all'oggetto che stiamo creando. Ad esempio:

function myFunc(){
this.StringValue = "This is a String";
}

var myObject = new myFunc();
var myObject2 = new myFunc();
alert(myObject2.StringValue); // displays "This is a String"



Adesso tutti gli oggetti myFunc avranno una proprieta' chiamata StringValue, assegnata con il valore iniziale "This is a String". Ogni oggetto, ovviamente, potra' continuare ad avere il suo valore distintivo per la proprieta' sopra indicata. In altre parole, una volta definita', possiamo cambiare il valore della proprieta' stessa senza intaccare le altre istanze:

unction myFunc(){
this.StringValue = "This is a String";
}

var myObject = new myFunc();
myObject.StringValue = "This is myObject's string";
var myObject2 = new myFunc();
alert(myObject.StringValue); // displays "This is myObject's string"
alert(myObject2.StringValue); // displays "This is a String"



Possiamo ottenere un risultato simile, utilizzando i parametri per il costruttore:

function myFunc(StringValue){
this.StringValue = StringValue;
}

var myObject = new myFunc("This is myObject's string");
var myObject2 = new myFunc("This is a String");
alert(myObject.StringValue); // displays "This is myObject's string"
alert(myObject2.StringValue); // displays "This is a String"



Nel costruttore di myFunc(), this.StrinValue si riferisce alla proprieta' che stiamo assegnando al nuovo oggetto appena creato, mentre StringValue si riferisce alla variabile locale della funzione passata come argomento. E per i metodi?

In aggiunta alle proprieta', gli oggetti possono avere dei metodi. Il metodo di un oggetto e' una funzione che puo' essere eseguita dall'oggetto stesso (una sua istanza). Proviamo a vederne un esempio. Creiamo un oggetto Circle. Per fare questo, dobbiamo definirne le funzioni e quindi renderle metodi del nostro nuovo oggetto.

function Circle(radius){
this.radius = radius;
}

var bigCircle = new Circle(100);
var smallCircle = new Circle(2);



Adesso definiamo alcune funzioni utili:

function getArea(){
return (this.radius*this.radius*3.14);
}

function getCircumference(){
var diameter = this.radius*2;
var circumference = diameter*3.14;
return circumference;
}



Attenzione ad un particolare: a cosa si riferisce this.radius? this si riferisce sempre all'oggetto corrente. Nel nostro caso: l'oggetto Circle. Quindi, this.radius, si riferirisce alla proprieta' radius dell'oggetto Circle. Come aggiungiamo queste funzioni ai nostri oggetti? Non e' molto difficile. Cambiamo il nostro costruttore:

function Circle(radius){
this.radius = radius;
this.getArea = getArea;
this.getCircumference = getCircumference;
}



Il codice sopra assegna le funzioni getArea e getCircumference all'oggetto Circle, rendendole a tutti gli effetti dei metodi proprietari dell'oggetto. Possiamo usare i metodi come normali funzioni. Per farlo, dobbiamo prima accedere all'oggetto nel quale si trova incapsulato il metodo:

alert(bigCircle.getArea()); // displays 31400
alert(bigCircle.getCircumference()); // displays 618
alert(smallCircle.getArea()); // displays 12.56
alert(smallCircle.getCircumference()); // displays 12.56



Molto interessante, certo. Ma se volessimo integrare tutto all'interno della funzione stessa? Esistono vari modi per ottenere questo scopo. Ad esempio, attraverso le funzioni interni. Una funzione interna e' una funzione all'interno della funzione. Carino, vero?

function Circle(radius){
function getArea(){
return (this.radius*this.radius*3.14);
}
function getCircumference(){
var diameter = this.radius*2;
var circumference = diameter*3.14;
return circumference;
}
this.radius = radius;
this.getArea = getArea;
this.getCircumference = getCircumference;
}



E' lo stesso codice visto sin'ora, ad eccezione delle funzioni che risultano spostate. Dal momento che le funzioni interne possono accedere alla variabili locali, all'interno delle nostre due funzioni, al posto di this.radius, potremmo usare direttamente radius. Semplicemente, dovrebbe essere possibile accedere alla variabile locale radius passata come argomento al costruttore Circle. Il nostro codice, quindi, potrebbe diventare qualcosa di questo tipo:

function Circle(radius){
function getArea(){
return (radius*radius*3.14);
}
function getCircumference(){
var diameter = radius*2;
var circumference = diameter*3.14;
return circumference;
}
this.radius = radius;
this.getArea = getArea;
this.getCircumference = getCircumference;
}



Ok. Adesso cambiamo il valore di radius di un oggetto e calcoliamone l'area:

bigCircle.radius=50;
alert(bigCircle.getArea()); // displays 31400



Attenzione! C'e' qualcosa di strano! Il valore ritornato e' 31400, al posto di 7850. Dove stiamo sbagliando? radius si riferisce al valore che abbiamo passato alla funzione costruttore, non al valore dell'oggetto. Quindi, quando cambiamo il raggio dell'oggetto, i metodi getArea e getCircumference, mantengono ancora il vecchio valore del raggio (quello calcolato una 10ina di esempi precedenti). Ecco perche' non possiamo usare semplicemente la variabile radius ma dobbiamo mantenerne la referenza all'oggetto corrente con this.

Esistono tre categorie di oggetti in JavaScript: Nativi, Host Objects e definiti dall'utente.

Gli oggetti nativi sono quegli oggetti forniti da JavaScript. Esempi di questi oggetti sono String, Number, Array, Image, Date, Math, etc.

Gli oggetti Host sono oggetti che sono forniti a JavaScript dall'ambiente browser. Esempi di questi oggetti sono window, document, forms, etc.

Infine, gli oggetti definiti dall'utente sono quegli oggetti definiti dal programmatore.

Uno dei concetti fondamentali in JavaScript e' che ogni elemento che puo' ospitare proprieta' e metodi e' un oggetto, eccezion fatta per i tipi di dati primitivi. Possiamo usare i costruttori built-in di JavaScript per creare nuovi oggetti:

var Image1 = new Image(50,100);
Image1.src = "myDog.gif";



Sopra, abbiamo creato un nuovo oggetto Image usando il costruttore nativo di Image con le seguenti proprieta':

width = 50
height = 100
src = "myDog.gif"

var myObj = new Object();



A questo oggetto base possiamo aggiungere metodi e proprieta'. Ogni oggetto in JavaScript deriva dall'oggetto base Object. Proviamo a dare un occhiata da vicino alla primitiva String:

var myString = "This is my string";
alert(myString); // displays "This is my string"
alert(typeof myString); // displays "string"



Ovviamente, possiamo rendere una stringa un oggetto, usando il suo costruttore:

var myString = new String("This is my string");
alert(myString); // displays "This is my string"
alert(typeof myString); // displays "object"



Adesso, abbiamo creato un'oggetto String. Possiamo fare lo stesso con Number e Boolean. Ma perche' dovremmo volerlo? Una volta resi oggetti, potremmo aggiungere delle proprieta' distintive per ogni oggetto. Un tipo primitivo, contiene proprieta' e metodi che gli sono stati assegnati in fase di costruzione, ma non puo' contenere proprieta' o metodi personali.

Cosi', una stringa contiene la properita' length e tanti metodi definiti nel tipo nativo String(), ma non puo' contenere proprieta' e metodi specializzati.

var myNumber = new Number(2);
myNumber.doubleIt = new Function("return this*2");
alert(myNumber.doubleIt()); // displays 4
alert(typeof myNumber); // displays "object"



In questo modo abbiamo creato un oggetto Number ed abbiamo definito un metodo doubleIt(). Il tipo di myNumber adesso e' diventato "object". Oggetti evoluti sono in grado di mantenere al loro interno proprieta' uniche e metodi. I dati primitivi come String, Boolean, Number, Undefined e Null, non possono farlo. Questa e' forse la piu' grande differenza tra i tipi primitivi e gli oggetti.