Microsoft's Internet Explorer browser has no built-in vector graphics machinery required for "loss-free" gradient background themes.
Please upgrade to a better browser such as Firefox, Opera, Safari or others with built-in vector graphics machinery and much more. (Learn more or post questions or comments at the Slide Show (S9) project site. Thanks!)
A metà degli anni ‘90, all’alba dell’esplosione del Web come IL servizio Internet per definizione, Brendan Eich entra in Netscape allo scopo di includere nel loro browser un interprete Scheme.
Al tempo in molti (tra cui Bill Joy di Sun) erano convinti e spingevano perchè il Web avesse un linguaggio di scripting da includere in formato sorgente all’interno delle pagine HTML…
Purtroppo tutto questo interessamento finì per essere controproducente:
La visione del management (di Netscape e di Sun) era che Java (e le applet) sarebbe stato il linguaggio usato dai programmatori per scrivere i componenti mentre questo nuovo linguaggio di scripting sarebbe stato utilizzato dagli scripter per scrivere il codice collante
The diktat from upper engineering management was that the language must “look like Java”. That ruled out Perl, Python, and Tcl, along with Scheme.
I’m not proud, but I’m happy that I chose Scheme-ish first-class functions and Self-ish (albeit singular) prototypes as the main ingredients. The Java influences, especially y2k Date bugs but also the primitive vs. object distinction (e.g., string vs. String), were unfortunate.
E’ fu così che il progetto inizialmente chiamato Mocha e successivamente chiamato Livescript raggiunse l’inclusione nella versione 2.0 del browser di Netscape:
Nel 1996 (lo stesso anno in cui Javascript è stato ufficialmente rilasciato con Netscape Navigator 2.0) Netscape sottopone Javascript alla ECMA International per la standardizzazione:
ECMAScript è il nome della standardizzazione del linguaggio contenuta nello standard ECMA-262 (arrivato alla terza revisione) a cui Javascript (Netscape) e JScript (Microsoft) cercano di rimanere compatibili.
Oltre allo standard ECMA-262 (alla revisione 3 attualmente in uso) sono presenti altri 3 standard riguardanti Javascript oltre alla revision 4 dello standard ECMA-262:
Inoltre da ECMAScript (oltre a Javascript e JScript) derivano altri linguaggi di scripting, oltre a essere presenti diverse varianti dello stesso dialetto:
La quarta revisione dello standard ECMA-262 aveva in programma l’introduzione di una notevole quantità di caratteristiche nel linguaggio:
Attualmente pero’ sono in programma solo alcune lievi modifiche (che porteranno ad ECMAScript 3.1) allo scopo di stabilizzare alcune caratteristiche già implementate in diverse implementazioni (come i getter e setter di Javascript 1.6 ad esempio) ed ad introdurre alcune feature utili a creare in ECMAScript stesso alcune delle caratteristiche di ECMAScript4 (ad esempio l’aggiunta del metodo Object.freeze() allo scopo di rendere un’oggetto non modificabile ulteriormente)
Per chi volesse provare al contrario le caratteristiche di ECMAScript4 può:
Come è possibile immaginare il cammino dello standard (ECMAScript) è sempre più lento di quello delle implementazioni dovendo mantenere la compatibilità con il passato e tra le varie versioni.
Ogni implementazioni (soprattutto quando utilizzata in ambiti diversi delle pagine Web) ha introdotto (e continua ad introdurre) correttivi o nuove caratteristiche al proprio dialetto.
Javascript, pur avendo le caratteristiche di un linguaggio General Purpose, è nato e vive gran parte della sua esistenza all’interno dei browser.
E’ il browser costituisce sin da allora il terreno di una violentissima battaglia, giocatasi inizialmente tra Netscape e Microsoft, ma che si è estesa a molti altri giocatori da allora, in cui lo scripting è stato utilizzata come arma per la conquista dello status di Standard De Facto
Il fallimento delle Applet fù quasi immediato e ora Javascript rappresenta IL linguaggio (e non il linguaggio di scripting) del Web, concorrendo solo con ActionScript (che è comunque pur sempre un dialetto ECMAScript).
Javascript è stato sin da subito confuso come un dialetto di Java, mentre in realtà i due linguaggi hanno in comune poco più della sintassi C-like.
Java | Javascript |
---|---|
Strongly Typed | Loosely Typed |
Static | Dynamic |
Static | Dynamic |
Class-based | Prototype-based |
Classes | Functions |
Constructor | Functions |
Methods | Functions |
Le caratteristiche dinamiche e scheme-like del linguaggio (e alcuni dei problemi di design iniziali del linguaggio) hanno reso Javascript una vera arma a doppio taglio in mano ai WebDesigner.
Di fatto fino alla nascita ufficiale di AJAX e JSON, Javascript era utilizzato per introdurre fastidiosi (e grezzi) effetti grafici alle pagine web sotto forma di spaghetti library e spaghetti code
Oltre ai problemi di incomprensione (con i programmatori Java e i Designer) il linguaggio si portava dietro alcune vere e proprie inconsistenze riguardo:
Per la prima volta dagli albori dell’home computing, un linguaggio di programmazione è presente (se pur sotto forma di diversi dialetti) su TUTTE le piattaforme.
Il termine AJAX è nato solo nel 2005 (nonostante le caratteristiche su cui è fondato erano per lo più presenti anche precedentemente) e da allora lo scenario del Web si è radicalmente modificato…
AJAX e il Web2.0 ha messo le basi ad una nuova fase per le WebApplication in cui il Web è appena entrato: Rich Internet Application
Microsoft con Silverlight, Adobe con AIR/Flex, Mozilla con XULRunner/Prism e Google con Chrome e Gears sono tutti pronti a cavalcare l’onda…
Inoltre Javascript ha trovato applicazione anche in ambiti diversi dal Browser, come lo scripting di applicazione e lo sviluppo desktop e server-side, ad esempio:
abstract boolean break byte case catch char class const continue debugger default delete do double else enum export extends false final finally float for function goto if implements import in instanceof int interface long native new null package private protected public return short static super switch synchronized this throw throws transient true try typeof var volatile void while with
NOTA : la maggior parte di queste parole riservate non sono utilizzate (sono riservate perchè utilizzate in Java :-D) ma non possono ( dovrebbero ) essere usate ne come nomi di variabili ne come attributi degli oggetti.
Tutti i linguaggi dinamici sono in genere dotati di un interprete interattivo con cui interagire con la Virtual Machine in maniere interattiva.
Javascript non fa ovviamente eccezione anzi sono molti i modi in cui è possibile ottenere una shell Javascript con cui interagire:
L’interprete più semplice con cui è possibile interagire è sicuramente Mozilla Spidermonkey, che nella sua versione più semplice ( js o smjs ) ci fornisce un interprete Javascript isolato dal browser e dagli altri componenti XPCOM:
rpl@ubik:~$ js js> var a = 5; js> print(a); 5 js>
In Ubuntu/Debian l’interprete interattivo spidermonkey è contenuto nel pacchetto spidermonkey-bin
In Javascript le variabili possono essere dichiarate esplicitamente (mediante la parola chiave var ) o essere utilizzate senza nessuna dichiarazione esplicita:
a = 5; var b = "a string"; var c;
NOTA : E’ assolutamente consigliabile dichiarare sempre esplicitamente le variabili, in quanto quelle utilizzate senza dichiarazione esplicita finiscono nello “scope” globale
Javascript non distingue tra numeri interi e float ma è dotato di un unico Number rappresentante internamente come un floating point a 64 bit (corrispondente ad un double Java):
1 1.0 1.0e3 // 1000 1.0e-3 // 0.001 1 == 1.0 // true 0.1 + 0.2 // 0.30000000000000004 0.2 + 0.09 // 0.29000000000000004 1 / 0 // Infinity 1 / -0 // -Infinity 1 / Infinity // 0 (9.3).toFixed(0) // 9 (9.6).toFixed(0) // 10 (10).toString() // "10"
Infinity rappresenta tutti i numeri più grandi di 1.79769313486231570e+308
Math.PI // 3.141592653589793 Math.sin(Math.PI/2) // 1 Math.floor(2.0+4.5) // 6 Math.ceil(2.0+4.5) // 7 parseInt("10.0000") // 10 parseInt("10.0JJJJ") // 10 parseInt("AAAAAA 10") // NaN 10 * "a" // NaN isNaN(10) // false isNaN(NaN) // true isNaN(Infinity) // false NaN == NaN // false
Math è un built-in object che raccoglie costanti e funzioni matematiche utili, mentre parseInt e isNaN sono funzioni built-in nello “scope” globale.
In Javascript le stringhe sono sequenze di caratteri (Unicode a 16-bit)
"Hello, World!!!" 'Hello,World!!!' "Javascript".length // 10 "Javascript".replace("script","") // "Java" "js".toUpperCase() // "JS"
In Javascript gli oggetti e i dizionari (HashTable) sono sinonimi e costituiscono uno dei concetti fondamentali del linguaggio:
var test_obj = { "key1": "1 value", "subobj 3": { property1: 3, property2: 5 } }; test_obj.key1 // "1 value" test_obj["subobj 3"].property1 // 3 test_obj["subobj 3"]["property2"] // 5 ...
var test_obj2 = {}; test_obj2["k1"] = 5 test_obj2.k2 = 6 test_obj2.k1 + test_obj2.k2 // 11 ...
Mentre la chiave (o nome) deve essere necessariamente una stringa, il valore puo’ essere un qualunque oggetto.
Un Array è uno speciale oggetto built-in:
var values = [ 10, 20, 30, 40 ]; values.length // 4 values[0] // 10 values[10] // undefined values[50] = 5 values.length // 51 values.join(" - ") // "10 - 20 - 30 - 40" values.slice(0,3) // [10,20,30] values.push(50) // 5 values.pop() // 50
Javascript è dotato di un tipo boolean, con valori (che costituiscono parole chiave) true and false. Tutti i valori possono essere convertiti in boolean secondo le seguenti regole:
Boolean("") // false Boolean(0) // false Boolean(NaN) // false Boolean(null) // false Boolean(undefined) // false Boolean("TEST") // true Boolean(55) // true Boolean({}) // true Boolean(Infinity) // true
Javascript è dotato di tutti gli operatori dei linguaggi con sintassi C-like;
+ - * / % // basic arithmentic operations = // assignment += -= ++ -- // increment / decrement operatos < > <= >= == != === !== // comparisons operators && || // boolean operators & | ^ ~ << >> >>> // bitwise operators
Gli operatori incremento e decremento possono essere usati come operatori sia in prefix che in postfix
L’operatore “+” viene utilizzato sia per la somma tra numeri che per la concatenazione di stringhe, in caso gli operatori siano di tipo diverso viene operata una conversione forzata, cosa che richiede particolare attenzione:
"p" + 1 + 1 // "p11" 1 + 1 + "p" // "2p"
Gli operatori di test di uguaglianza in Javascript richiedono ancora più attenzione in quanto le versioni standard ( “==” / “!=” ) operano una conversione forzata e silenziosa che potrebbe non dare i risultati che ci si aspetterebbe:
'' == '0' // false 0 == '' // true 0 == '0' // true false == 'false' // false false == '0' // true false == undefined // false false == null // false null == undefined // true ' \t\r\n ' == 0 // true
NOTA : per questo motivo è sempre consigliabile usare gli operatori “===” e “!==” che non effettuano conversioni forzate e silenziose.
typeof ed instanceof sono due particolari operatori il cui compito è rispettivamente identificare il tipo di un valore e verificare se è istanza di un determinato prototipo:
typeof 10 // number typeof "test string" // string typeof true // boolean typeof {} // object var c = {}; c instanceof Object // true
Ci sono alcuni valori in Javascript che hanno comportamente particolarmente poco amichevoli:
Se questo non bastasse… Javascript ha ereditato da Java la differenza fra primitiva e istanza del relativo oggetto:
typeof "test string" // 'string' typeof new String("test string") // 'object' "test" instanceof String // false new String("test string") instanceof String // true typeof true // 'boolean' typeof new Boolean(true) // 'object' typeof 10 // 'number' typeof new Number(10) // 'object'
NOTA : per questo motivo è sempre meglio utilizzare esclusivamente le primitive per i tipi dati primitivi (string, boolean, number e function)
Le strutture di controllo di Javascript sono simili alle strutture di controllo classiche dei linguaggi C-like:
var score = 100, message; if (score < 50) { message = "too low!!!"; } else if (score > 100) { message = "you are god!!!"; } else { message = "good score!"; } myvar = obj && obj.name; myvar = name || "default value"; myvar = score >= 20 ? 20 : score;
Lo switch (analogo a quello presente nei linguaggi C-like) confronta l’espressione contenuta nella switch con l’espressione contenuta nelle singole clausole case mendiante l’operatore ===:
switch(action_type) { case 'left_button': open_in_new_tab(); break; case 'right_button': open(); break; default: alert("unknown action type: "+action_type); }
while (action_type != 'quit') { // do something } do { // do something } while (action_type != 'quit')
for (var i = 0; i < 5; i++) { // do something }
for…in e for each…in sono due varianti della struttura di controllo for utili per iterare su Array ed Object:
for (var i in [1,2,3]) { print(i); } for each (var i in [1,2,3]) { print(i); } var obj { key1: "value1", key2: "value2" } for (var i in obj) { print(i); } for each (var i in obj) { print(i); }
Javascript è dotato di un sistema di gestione delle eccezioni con una sintassi del tutto simile agli altri linguaggi C-like:
// ReferenceError: unknown_function is not defined try { unknown_function(); } catch(error) { print(error); } try { throw new Error("My personal ERROR"); } catch(error) { print(error); }
try { throw new Error("My personal ERROR"); } finally { print("cleaning!"); }
Le funzioni sono sicuramente, insieme agli Object/Hashtables, uno dei costrutti fondamentali per Javascript.
function sum(a,b) { return a+b; }; var mult = function mult(a,b) { return a*b; }; sum(2,3); // 5 sum(2,3,4); // 5 <----- I parametri aggiuntivi sono ignorati mult(2,10); // 10
In Javascript le variabili dichiarate con var non sono legate al block scope come negli altri linguaggi C-like, ma sono in realtà legate al function scope:
function test_var_scope() { var a = 5; for (var i = 0; i < a; i++) { var tmp = 200; // do something } print(a); print(i); print(tmp); }
Per questo motivo è opportuno dichiarare tutte le variabili utilizzate in una funzione in testa, oltre a non dimenticarsi di dichiararle con var …
function add(a,b) { tot = a+b; return tot; } test = add(5,6); test // 11 tot // 11 <---- tot è finita nel global scope!!!
In Javascript il nome delle funzioni è opzionale, per cui le funzioni anonime si dichiarano in maniera analoga alle funzioni dotate di un nome.
Inoltre all’interno dello scope di una funzione è possibile accedere ad una variabile simile ad un array che ci da accesso ai parametri e ad un riferimento alla funzione corrente (utile in caso di funzioni anonime):
add = function (a,b) { return a+b; }; tot = function (a,b) { return a*b; }(5,3); count_parameters = function () { return arguments.length; } fact = function (n) { if (n == 1) return 1; return n*arguments.callee(n-1); } fact(3) fact(4)
Le funzioni in Javascript sono First class object (possono essere passate come parametri e ritornate come valori dalle funzioni) e sono dotate di proprietà e metodi come gli altri oggetti.
var add = function add(a,b) { return a+b; } add.length // 2 (il numero di parametri) add.call(null,2,3); add.apply(null,[2,3]); add.toString() var obj = { sum: function (a,b) { return a+b; } mult: function (a,b) { return a*b; } } obj.sum(5,6); obj["mult"](3,2);
In Javascript è consentito dichiarare una funzione all’interno di un’altra funzione:
function do_alot_of_stuff(param1, param2) { var tot; // do stuff... tot = sum(param1,param2); // do other stuff... return tot; function sum(a,b) { return a+b; }; }
NOTA: non ha importanza dove vengono dichiarate le inner function , è come se tutte le funzioni fossero valutate all’inizio della funzione.
Le closure sono una importantissima caratteristica delle funzioni in Javascript, sulla quale si poggiano sia i meccanismi di Event Handling usati solitamente nello scripting Web, che molte delle tecniche per completare la OOP di Javascript o ovviare ad alcuni dei suoi difetti di design.
Le closure in Javascript sono costituite dalle normali funzioni, le quali portano con se il lexical scope nelle quali sono state definite.
function mult(n) { return function(a) { return a*n }; }; double = mult(2); double(5); // 10 double(4); // 8 half = mult(0.5); half(5); // 2.5 half(4); // 2 mult(0.5)(4); // 2
var test_obj = function() { var private_var = 100; return { get_private_var: function() { return private_var; }, step: function() { private_var++;} } }(); test_obj.get_private_var(); test_obj.step(); test_obj.get_private_var();