javascript
Transcript
javascript
javascript crash course Vittorio Maniezzo – University of Bologna 1 javascript JavaScript è un linguaggio di programmazione: • funzionale • interpretato • orientato agli oggetti • con garbage collection. JavaScript è stato sviluppato in 10 giorni nel maggio 1995 da Brendan Eich, che lavorava a Netscape, come linguaggio di scripting HTML per il proprio browser Navigator 2 Vittorio Maniezzo – University of Bologna 2 1 JavaScript JavaScript è il linguaggio del web. Può essere usato per: • Implementare una applicazione con memorizzazione dati locale o remota. • Implementare un componente front-end per una applicazione web distribuita, con dati in remoto gestiti da un componente server-side (PHP, Java, C#). • Implementare una applicazione web distribuita completa, dove sia il front-end che il back-end sono programmi JavaScript (es., con NodeJS). • Arricchire una pagina web con codifica dinamica del DOM. Vittorio Maniezzo – University of Bologna 3 Il linguaggio in un lucido // Un commento var x; // x = 3 + y; // foo(x, y); // obj.bar(3); // in linea, inizia con due slash dichiarazione di una variabile assegnamento di un valore a una variabile`x` chiamata della funzione`foo`con parametri`x`e y` chiamata del metodo`bar`dell’oggetto`obj` if (x === 0) { x = 123; } // condizionale // definizione della funzione foo`con parametri`a`e`b` function foo(a, b) { return a + b; } Vittorio Maniezzo – University of Bologna 4 2 Eseguire il codice Il codice può essere messo in esecuzione in due modi: • In un browser, inserendolo come script in una pagina html <head> <script type="text/javascript"> function print(text) { document.querySelector("#output").innerHTML += text + "\n"; } </script> <script type="text/javascript" src="code.js"></script> </head> <body> <pre id="output"></pre> <script type="text/javascript"> print("Ciao bello!"); </script> </body> • Standalone, ad esempio richimandolo da node.js Vittorio Maniezzo – University of Bologna 5 Tipi di dati • Tre tipi di dati primitivi: string, number e boolean. • Tre tipi by reference: object, array e function. • Gli array e le funzioni sono solo oggetti particolari. • I tipi non sono dichiarati e non sono controllati (JavaScript non è compilato). • La conversione dei tipi (casting) è fatta automaticamente. Vittorio Maniezzo – University of Bologna 6 3 Valori Il valore di una variabile può essere • Un dato: una stringa, un numero, un booleano, • Un puntatore a un oggetto (reference): riferimento a un oggetto, a un array, o a una funzione, • Uno dei due valori speciali: undefined o null. Vittorio Maniezzo – University of Bologna 7 Valori numerici Tutti i valori numerici sono sempre rappresentati come 64-bit floating point con esponente opzionale (es. 3.1e10). Non c’è distinzione fra integer e floating point. Per assicurarsi che un numero sia un integer si può applicare la funzione parseInt (numero come stringa e taglia al .) Se una espressione numerica non produce un numero, il suo valore diventa NaN ("not a number"). Vittorio Maniezzo – University of Bologna 8 4 Valori Booleani Due letterali Booleani: true e false Gli opertori Booleani sono quelli del c: ! per NOT, && per AND, || per OR. Un valore non Booleano usato in una espressione Boolean viene convertito in un valore Booleano così: • Stringa vuota, numero 0, undefined e null, sono false, • Tutti gli altri valori sono true. Per il test, due possibilità: triple equality symbol === e !== oppure double equality == e !=. I tripli evitano casting automatico, (2 === "2") è false mentre (2 == "2") è true Vittorio Maniezzo – University of Bologna 9 Array e oggetti: inizializzazone Array: può essere fatta assegnando il letterale di array vuoto, var a = [], o invocando il costruttore della classe Array(), var a = new Array(). Oggetti: può essere fatta assegnando il letterale di oggetto vuoto, var o = {} , o invocando il costruttore della classe Object(), var o = new Object(). Il letterale oggetto vuoto, {}, non è vuoto, ma contiene proprietà e metodi ereditati da Object.prototype. Per cerare un oggetto davvero vuoto si usa Object.create(null). Vittorio Maniezzo – University of Bologna 10 5 Scope delle variabili Nella versione ECMAScript 5 di JavaScript, ci sono solo due scope: globale e funzione, non il blocco. Dichiarare una variabile in un blocco è ammissibile, ma è come dichiararla a inizio funzione (hoisting): function foo() { for (var i=0; i < 10; i++) { ... } } E’ come: function foo() { var i=0; for (i=0; i < 10; i++) { ... } } Tutte le variabili non dichiarate vengono assunte avere uno scope globale. Evitarle! (magari in una libreria che linkate ne usano una con lo stesso nome) Vittorio Maniezzo – University of Bologna 11 Strict mode Lo strict mode istanzia un più stretto error checking a runtime. Per esempio, in strict mode tutte le variabili devono essere dichiarate e un assegnamento a una variabile non dichiarata genera una eccezione. Per attivare lo strict mode si inserisce come prima linea del file JavaScript file o dello <script> : "use strict"; Vittorio Maniezzo – University of Bologna 12 6 Oggetti A differenza dell’OO standard, in JS gli oggetti non sono necessariamente istanze di una classe. Gli oggetti possono essere creati senza istanziare una classe: var obj1 = { lastName:“Maniezzo", firstName:“Vittorio"}; var obj2 = Object.create( null ); // oggetto vuoto, senza slot Un oggetto è un insieme di coppie attributo-valore (name-valuepairs), dette slot, che possono essere: • proprietà (property slot), obj1.firstName = "pippo"; • funzioni (function slot), obj1.getName: function () { return obj.firstName; } • chiavi di una mappa associativa (key-value slot). Vittorio Maniezzo – University of Bologna 13 Proprietà e metodi L’attributo in un property slot può denotare: • Una proprietà data-valued, quindi il valore è (una espressione che calcola) un dato; • Una proprietà object-valued, quindi il valore è un riferimento ad un oggetto o, più in generale, una espressione che definisce un oggetto. L’attributo in un method slot denota una funzione (un metodo), il suo valore è il testo con il sorgente della funzione. Vittorio Maniezzo – University of Bologna 14 7 Accesso alle proprietà Le proprietà degli oggetti possono essere utilizzate in due modi: Con la notazione a punto (come c#/c++/Java): obj1.lastName = “Maniezzo" Con la notazione tipo mappa associativa: obj1["lastName"] = “Maniezzo" Vittorio Maniezzo – University of Bologna 15 Oggetti: utilizzo Casi d'uso : • record: è un insieme di property slots: var myRecord = {firstName:"Tom", lastName:"Smith", age:26} • map ('associative array'): permette il recupero di valori sulla base di chiavi (stringhe !! ): var numeral2number = {"one":"1", "two":"2", "three":"3"}. • untyped object: non è istanza di una classe, ma può avere property slot e function slot: var person1 = { lastName: "Smith", getInitial: function () { return this.lastName.charAt(0); } }; • namespace: un untyped object referenziato da una variabile globale che rappresenta un namespace prefix: var myApp = { model:{}, view:{}, ctrl:{} }; • typed object: instanzia una classe che è definita o da un costruttore C o da un factory object F. Vittorio Maniezzo – University of Bologna 16 8 Array Un array, in realtà un'array list, è una lista dove ogni elemento può essere individuato con un indice numerico. Inizializzazione: var a = [1,2,3]; Gli array possono crescere dinamicamente, si possono usare indici maggiori della lunghezza dell'array. Per es., con un array lungo 3, si può assegnare il quinto elemento a[4] = 7; Il contenuto può essere visitato con un for: for (i=0; i < a.length; i++) {...} Vittorio Maniezzo – University of Bologna 17 Array Per aggiungere un elemento in coda all'array: a.push(newElement); per cancellare un elemento in posizione i dall'array a: a.splice(i, 1); Per cercare l'indice di un elemento di valore i nell'array a (-1 se non lo trova) : if (a.indexOf(v) > -1) ... Per visitare tutti gli elementi, for o foreach (se array piccolo): var i=0; for (i=0; i < a.length; i++) { ... } a.forEach( function (elem) { console.log( elem); }) Per clonare un array a: var clone = a.slice(0); Vittorio Maniezzo – University of Bologna 18 9 Map Una map ('hash map', 'associative array') implementa un mapping da chiavi a valori associati. Le chiavi sono stringhe: var myTrad = { "my house": “mia casa", “my boat“ : “mia barca"} La funzione Object.keys(m) permette di elencare tute le chiavi di una map: var i=0, key="", keys=[]; keys = Object.keys( myTrad); for (i=0; i < keys.length; i++) { key = keys[i]; alert(‘La traduzione di '+ key +' è '+ myTrad[key]); } Vittorio Maniezzo – University of Bologna 19 Map Per aggiungere una nuova entry: myTrad["my car"] = “mia auto"; Per cancellare una entry: delete myTrad["my boat"]; Per cercare un elemento di chiave data: if ("my bike" in myTrad) ... Per passare tutti gli elementi di una map m, prima Object.keys poi for oppure forEach: var i=0, key="", keys=[]; keys = Object.keys(m); for (i=0; i < keys.length; i++) { key = keys[i]; console.log(m[key]); } oppure, se m è piccola: Object.keys(m).forEach( function (key) {console.log(m[key]); }) Vittorio Maniezzo – University of Bologna 20 10 Funzioni Le funzioni sono oggetti. Possono essere assegnate a variabili, passate come argomenti ad alte funzioni, ritornate da funzioni, avere proprietà e venire cambiate dinamicamente. La forma generale della definizione di una funzione è un assegnamento ad una variabile: var myFunction = function theNameOfMyFunction () {...} dove theNameOfMyFunction è opzionale, se non lo si mette la funzione è anonima. Le funzioni sono invocate attraverso la variabile che referenzia la funzione, cioè via myFunction() e non theNameOfMyFunction(). Vittorio Maniezzo – University of Bologna 21 Funzioni anonime (lambda) Esempio di funzione anonima passata come argomento ad un’altra funzione: ordinamento di un array list di coppie di numeri in ordine lessicografico. La funzione di confronto deve ritornare <0 se primo argomento minore del secondo, >0 se maggiore e =0 se uguali. var list = [[1,2],[1,3],[1,1],[2,1]]; list.sort( function (el1,el2) { return ((el1[0] === el2[0]) ? el1[1]-el2[1] : el1[0]-el2[0]); }); Vittorio Maniezzo – University of Bologna 22 11 Funzioni La dichiarazione di una funzione ha la forma: function unaFunzione() {...} o, equivalentemente: var unaFunzione = function unaFunzione() {...} Entrambe creano sia una funzione di nome unaFunzione che una variabile unaFunzione che la referenzia. Le funzioni possono avere funzioni interne. La closure in JavaScript permette alla funzione interna di accedere a tutte le variabili di quella esterna, al momento della sua creazione. Esempio: var add = (function () { var counter = 0; return function () {return counter += 1;} })(); Vittorio Maniezzo – University of Bologna 23 Classi In JavaScript non c’è una gestione esplicita delle classi, ma le si può definire in due modi diversi: • attraverso una funzione costruttore che permette di creare nuove istanze tramite l’operatore new. • attraverso un factory object che usa il metodo predefinito Object.create per creare le nuove istanze. Inoltre, gli oggetti possono essere creati anche senza creare un'istanza di una classe, nel qual caso non sono tipizzati. In fase di esecuzione, metodi e proprietà possono sempre essere aggiunti a o rimossi da qualsiasi oggetto e classe. Vittorio Maniezzo – University of Bologna 24 12 Classi: costruttore Una classe con costruttore può essere definita come segue. 1. Definire un costruttore che specifica le proprietà della classe e assegna i valori passati coi parametri del costruttore: function Person(cognome) { this.lastName = cognome; } 2. Definire i metodi delle istanze come method slot della proprietà prototype del costruttore: Person.prototype.getInitials = function () { return this.lastName.charAt(0); } 3. Definire i metodi statici della classe come method slot del costruttore Person.checkName = function (n) { ... } Vittorio Maniezzo – University of Bologna 25 Classi: costruttore Una istanza si crea attraverso una new: var pers1 = new Person"Maniezzo"); I metodi sono invocati sull’oggetto tramite la notazione col punto: alert("The initial of the person is: " + pers1.getInitials()); Il tipo (classe) dell’oggetto creato può essere recuperato tramite pers1.constructor.name che ritorna "Person". L’eredità è garantita sui metodi, che vengono ereditati attraverso la proprietà prototype del costruttore. Le altre proprietà non sono ereditate nello stesso modo. Vittorio Maniezzo – University of Bologna 26 13 Classi: factory based Si definisce un oggetto Person (che rappresenterà una classe) che contiene un metodo speciale, create, che invoca il metodo predefinito Object.create per crearne le istanze: var Person = { typeName: "Person", properties: { firstName: {range:"NonEmptyString", label:"First name", writable: true}, lastName: {range:"NonEmptyString", label:"Last name", writable: true} }, methods: { getInitials: function () { return this.firstName.charAt(0) + this.lastName.charAt(0); } }, create: function (slots) { var obj = Object.create( this.methods, this.properties); Object.defineProperty( obj, "type", {value: this, writable: false, enumerable: true}); Object.keys( slots).forEach( function (prop) { if (prop in this.properties) obj[prop] = slots[prop]; }) return obj; } }; Una istanza della classe Person viene generate invocandone il metodo create: var pers1 = Person.create( {firstName:“Vit", lastName:“Man"}); Vittorio Maniezzo – University of Bologna 27 Callback functions Una funzione di callback è una funzione che viene passata a un'altra funzione come parametro, ed eseguita all'interno della funzione esterna. Passando la funzione di callback come argomento, se ne passa solo la definizione: la funzione di callback non viene eseguita immediatamente ma ri-chiamata (called back) in un punto all'interno del corpo della funzione che lo contiene (la callback è una closure). var friends = [“Anna", “Bea", “Carla"]; friends.forEach(function (eachName, index){ console.log(index + “ - " + eachName); }); (forEach ha due overload, con 1 o 2 parametri) Vittorio Maniezzo – University of Bologna 28 14 Callback functions Le callback sono molto utili per gestire eventi asincroni function getScript(callback) { var script = document.createElement('script'); script.src = 'http://da_qui_il_codice'; document.getElementsByTagName('head')[0].appendChild(script); //dopo l’evento load dello script, si esegue la callback script.onload = callback; }; // chiamata nel codice getScript(function(){ console.log(“caricato lo script"); }); Vittorio Maniezzo – University of Bologna 29 Callback functions La funzione non deve per forza essere anonima, in questo caso si può passare il nome. // funzione che stampa a console function logStuff (userData) { console.log(userData); } // due parametri, il secondo è una callback function function getInput (options, callback) { callback (options); } // chiamata nel codice getInput ({nome:“Pippo", anni:“103"}, logStuff); Vittorio Maniezzo – University of Bologna 30 15 EcmaScript 6 ECMAScript 6 è la versione di giugno 2015 dello standard ECMAScript (http://www.ecma-international.org/ecma262/6.0/ , ma c’è già anche la 7), molte modifiche sintattiche. Implementazione avanzata in molti browser: http://kangax.github.io/compat-table/es6/ Nuovi elementi in ES6: arrows classes enhanced object literals template strings destructuring default + rest + spread let + const iterators + for..of generators Unicode modules module loaders proxies symbols Map, set (weakmap, weakest) subclassable built-ins promises math,number,string,array,object APIs binary and octal literals reflect api tail calls Vittorio Maniezzo – University of Bologna 31 Block−scoped variables for (let i = 0; i < a.length; i++) { let x = a[i] … } Vittorio Maniezzo – University of Bologna 32 16 Arrow Functions Sintassi più espressiva per le clusure, usano l’operatore => per definire delle lambda. Sintatticamente molto simili all’equivalente C#, // Statement bodies var nums = [1,2,3]; nums.forEach(v => { if (v % 5 === 0) fives.push(v); }); Vittorio Maniezzo – University of Bologna 33 Destructuring assignment Object and array matching Destrutturazione di array e oggetti in variabili durante un assegnamento // array var list = [1, 2, 3] var [a, , b] = list var [x, ...y] = ['a', 'b', 'c']; // x = 'a', y = ['b', 'c'] ... è l’operatore resto /* object: se il valore di una property è assegnato a una variabile col nome uguale a quello della property, lo si può omettere */ var {x, y} = {x: 11, y: 8}; console.log(x) // 11 console.log(y) // 8 Vittorio Maniezzo – University of Bologna 34 17 Moduli: export/import di valori Supporto a export/import di valori da/per moduli senza invader i namespace. // lib/math.js export function sum (x, y) { return x + y; } export var pi = 3.141593 // someApp.js import * as math from "lib/math" console.log("2π = " + math.sum(math.pi, math.pi)) // otherApp.js import { sum, pi } from "lib/math" console.log("2π = " + sum(pi, pi)) Vittorio Maniezzo – University of Bologna 35 Classi Più intuitive, stile OOP senza necessitare di librerie. class Shape { constructor (id, x, y) { this.id = id; this.move(x, y); } move (x, y) { this.x = x; this.y = y; } } Vittorio Maniezzo – University of Bologna 36 18