Upload
manuel-scapolan
View
1.172
Download
2
Embed Size (px)
DESCRIPTION
Continuando a parlare di JavaScript ...
Citation preview
OO JAVASCRIPT
Sviluppo applicazioni web e linguaggio HTML
LEZIONE 03
JavaScript? E’ un linguaggio di
scripting Object-Oriented
interpretato da un
browser web
Tutto in JavaScript è un
oggetto, ovvero una
rappresentazione
di un concetto o di
una entità del
mondo reale
sotto forma di
dati + azioni
…
var comodino = new Object();
comodino.cassetti = 3;
comodino.colore = ‘Bianco’;
comodino.altezza = 400; // in millimetri
comodino.larghezza = 500;
comodino[‘profondità’] = 400;
…
…
var comodino = {
cassetti : 3,
colore : ‘Bianco’,
altezza : 400,
larghezza : 500,
‘profondità’ : 400
}
…
Notazione letterale
var a = [ ];
typeof a; // risultato: "object“
var misc = [ 3, ‘Bianco’, 400, 500, 400 ];
misc.length; // risultato: 5
JSON
var comodino = {
“cassetti” : 3, “colore” : “Bianco”,
“misure” : [
{ “altezza” : 400 },
{ “larghezza” : 500 }, { “profondità” : 400 }
]
}
… ma per oggetto si intende forse
istanza di una
classe?
Classi!? In JavaScript non esistono
function Comodino(colore, cassetti) {
this.colore = colore;
this.cassetti = cassetti;
return this; // facoltativo
};
var comodino = new Comodino(‘Bianco’, 3);
Il costruttore è una funzione
Apriamo una
parentesi sulle
funzioni
Le funzioni sono oggetti
un contesto di
esecuzione : this
un insieme di valori
passati come
parametri : arguments
un campo contenente
tutto il codice
function sum( ) {
var total = 0;
var count = arguments.length;
for(var i = 0; i < count; i++) {
total += arguments[i];
}
return total;
}
variadic function
sum( ); // risultato: 0
sum(1, 2, 3, 5, 6, 7); // risultato: 24
Una funzione assegnata
alla proprietà di un
oggetto è un metodo
var comodino = {
cassetti : 3,
colore : ‘Bianco’,
altezza : 400,
larghezza : 500,
‘profondità’ : 400,
dimensioni : function( ){
return this.larghezza + ‘x’ + this.altezza
+ ‘x’ + this[‘profondità’] + ‘ mm’;
}
}
anonymous function
…
comodino.dimensioni( );
// risultato: 500x400x400 mm
Lo scope di una variabile è
limitato alla funzione nella
quale la variabile è stata dichiarata
var zoccolo = ’80 millimetri’;
var altezza = function(comodino) {
var altezza_zoccolo = parseInt(zoccolo);
return comodino.altezza
+ altezza_zoccolo;
};
variabile globale Accessibile da tutta
l’applicazione
var zoccolo = ’80 millimetri’;
var altezza = function(comodino) {
var altezza_zoccolo = parseInt(zoccolo);
return comodino.altezza
+ altezza_zoccolo;
};
variabile locale
Accessibile solo
all’interno della funzione
function foo( ) {
if ( true) {
var zoccolo = ‘80 millimetri’;
}
alert(zoccolo);
};
foo( );
Accessibile solo
all’interno della funzione
il contesto
di esecuzione di una funzione
dipende dal
tipo di chiamata
Se la funzione è chiamata
come metodo, il contesto è
l’oggetto al quale appartiene
var comodino = {
…
dimensioni : function( ){
return this.larghezza + ‘x’ + this.altezza
+ ‘x’ + this[‘profondità’] + ‘ mm’;
}
}
Se la funzione viene definita
globalmente a livello di
applicazione, il contesto è
l’oggetto globale (window)
var a = 1;
var b = 2;
var c = sum(this.a, this.b);
Per una funzione interna il
contesto è quello della
funzione esterna
var a = 5;
var foo = function( ) {
var a = 0;
var f = function(b){
a += b;
};
f(5);
return a;
};
foo(); // risultato: 5
var a = 5;
var foo = function( ) {
var a = 0;
var f = function(b){
this.a += b;
};
f(5);
return a;
};
foo(); // risultato: 0
a = 10
Cosa succede se chiamiamo una funzione
globale all’interno di una funzione interna?
var value = 0;
var obj = { value : 5 };
obj.add = function (a) {
var inner = function (){
this.value = sum(this.value, a);
};
inner();
return this.value;
};
obj.add(3);
Qual è il risultato
di obj.add(3)?
Cosa succede se chiamiamo una funzione
globale all’interno di una funzione interna?
var value = 0;
var obj = { value : 5 };
obj.add = function (a) {
var inner = function (){
this.value = sum(this.value, a);
};
inner();
return this.value;
};
obj.add(3);
Si riferisce all’oggetto
globale
Cosa succede se chiamiamo una funzione
globale all’interno di una funzione interna?
var value = 0;
var obj = { value : 5 };
obj.add = function (a) {
var inner = function (){
this.value = sum(this.value, a);
};
inner();
return this.value;
};
obj.add(3);
Si riferisce all’oggetto
globale
Creo una variabile that nel metodo esterno per
passare alla funzione globale il contesto locale
var value = 0;
var obj = { value : 5 };
obj.add = function (a) {
var that = this;
var inner = function (){
that.value = sum(that.value, a);
};
inner();
return this.value;
};
obj.add(3);
E se volessimo cambiare il
contesto di esecuzione? Con
il metodo apply si può!
var obj = { value : 5 };
obj.multiply = function(a){
return this.value * a;
};
obj.multiply(3);
var value = 2;
obj.multiply.apply(this, [3]);
parametri:
- nuovo contesto
- array di argomenti
Cosa succede se nel creare un oggetto mi
dimentico della parola chiave new?
var value = 0;
function Obj(value){
this.value = value;
};
var obj = Obj(5);
obj.value = 10;
Quale valore ho
modificato?
Cosa succede se nel creare un oggetto mi
dimentico della parola chiave new?
var value = 0;
function Obj(value){
this.value = value;
};
var obj = Obj(5);
obj.value = 10;
Quale valore ho
modificato?
Lo scope delle variabili di una
funzione viene determinato al
momento della sua definizione e
non quando questa viene eseguita
function foo( ) {
var a = 123;
bar( );
};
function bar( ) {
return a;
};
foo(); // risultato: undefined
In esecuzione il ritorno
di “a” da parte di bar()
avviene dopo la dichiarazione di “a”
ma il risultato è …
Lo scope di bar() al
momento della sua definizione è globale
function foo( ) {
var a = 123;
bar( );
};
function bar( ) {
return a;
};
foo(); // risultato: undefined
function foo( ) {
var a = 123;
var bar = function( ) {
return a;
};
return bar();
};
foo(); // risultato: 123
Lo scope di bar() al momento
della sua definizione è lo stesso di foo()
Le funzioni interne possono
accedere alle variabili e ai
parametri delle funzioni nelle
quali sono definite
var a = 14;
function foo( ) {
var b = 10;
function bar( ) {
var c = 3;
alert(a+b+c);
};
bar( );
};
foo(); // mostra 27
c
b
a
bar scope
foo scope
global scope
“A closure is formed when one of
those inner functions is made
accessible outside of the function in
which it was contained, so that it
may be executed after the outer
function has returned.”
http://jibbering.com/faq/notes/closures/
var obj = function ( ) {
var value = 1;
return {
add : function (a) { value += a; },
getValue : function ( ) { return value; }
}
}( );
obj.add(5);
obj.getValue(); // risultato: 6
function foo(arg) {
var bar = function(){
return arg;
};
arg++;
return bar;
};
var bar = foo(5);
bar(); // risultato: 6
function foo( ) {
var i, array = [ ];
for(i = 0; i < 3; i++) {
array[ i ] = function( ) {
return i;
};
}
return array;
};
var a = foo( );
a[0]( ); // risultato: 3
a[1]( ); // risultato: 3
a[2]( ); // risultato: 3
La funzione anonima
mantiene un riferimento allo
scope del parent
Le tre funzioni puntano
entrambe al valore di “i” terminata l’esecuzione di foo e quindi del loop
function foo( ) {
var i, array = [ ];
for(i = 0; i < 3; i++) {
array[ i ] = ( function( j ) {
return function( ) { return j; }
})( i );
}
return array;
};
var a = foo( );
a[0]( ); // risultato: 0
a[1]( ); // risultato: 1
a[2]( ); // risultato: 2
La funzione anonima
accetta “i“ come parametro
Adesso ogni funzione ha
“i” nel suo scope con il valore i-esimo del loop
// funzione con una callback come parametro
function foo(a, b, callback) {
var n = (a * b) + b;
callback(n);
}
foo (5, 2, function(n) {
alert("callback result: " + n);
});
anonymous function
Chiusa la
parentesi sulle
funzioni
In JavaScript non esistono classi …
function Comodino(colore, cassetti) {
this.colore = colore;
this.cassetti = cassetti;
return this; // facoltativo
};
var comodino = new Comodino(‘Bianco’, 3);
Il costruttore è una funzione
Ogni funzione ha una proprietà
prototype che è il prototipo degli
oggetti creati con quella funzione
typeof Comodino.prototype // risultato: object
Il prototipo mantiene in una proprietà
constructor il riferimento alla funzione
che lo ha generato
typeof Comodino.prototype.constructor
// risultato: Function
Comodino.prototype.constructor
// risultato: Comodino( )
Invece che inizializzare gli oggetti nel
costruttore lo posso fare con il prototype
function Comodino( ) { };
Comodino.prototype.cassetti = 3;
Comodino.prototype.colore = ‘Bianco’;
…
var comodino = new Comodino( );
comodino.cassetti // risultato: 3
Comodino.prototype.isProtoypeOf(comodino); // true
L’aggiunta di una proprietà nel prototipo
si riflette su tutti gli oggetti creati con
quel prototipo
function Comodino( ) { };
Comodino.prototype.cassetti = 3;
Comodino.prototype.colore = ‘Bianco’;
var comodino1 = new Comodino( );
var comodino2 = new Comodino( );
Comodino.prototype.altezza = 400;
comodino1.altezza; // risultato: 400
comodino2.altezza; // risultato: 400
augmentation
OK, ma non voglio mica tutti gli oggetti
uguali!
…
Comodino.prototype.altezza = 400;
comodino1.altezza; // risultato: 400
comodino2.altezza; // risultato: 400
comodino1.altezza = 300;
comodino1.altezza ; // risultato: 300
comodino2.altezza; // risultato: 400
comodino1.hasOwnProperty(‘altezza’); // true
comodino1.hasOwnProperty(‘altezza’); // false
shadowing
function Comodino(colore, cassetti) {
this.colore = colore;
this.cassetti = cassetti;
};
Comodino.prototype = {
constructor : Comodino,
altezza : 400
};
In JavaScript esiste
l’ereditarietà?
Interface inheritance
Implementation inheritance
NO
SI
Prototype Chaining
Classical Inheritance
Pseudoclassical Inheritance
Prototypal Inheritance
Prototype Chaining
Classical Inheritance
Pseudoclassical Inheritance
Prototypal Inheritance
function Mobile( ) {
this.materiali = [ ‘Legno’ ];
}
function Comodino( ) {
this.colore = ‘Bianco’;
}
// Comodino eredita da Mobile
Comodino.prototype = new Mobile();
var comodino = new Comodino( );
comodino.materiali[0]; // risultato: “Legno”
prototype chaining
...
// Comodino eredita da Mobile
Comodino.prototype = new Mobile();
var comodino1 = new Comodino( );
var comodino2 = new Comodino( );
comodino1.materiali.push(‘Vetro’);
comodino1.materiali; // ”Legno, Vetro”
comodino2.materiali; // ”Legno, Vetro”
Le proprietà impostate nel costruttore
diventano proprietà di prototipo e
quindi condivise
Prototype Chaining
Classical Inheritance
Pseudoclassical Inheritance
Prototypal Inheritance
function Mobile( ) {
this.materiali = [ ‘Legno’ ];
}
function Comodino( ) {
this.colore = ‘Bianco’;
Mobile.call(this);
}
var comodino1 = new Comodino( );
var comodino2 = new Comodino( );
comodino1.materiali.push(‘Vetro’);
comodino1.materiali; // ”Legno, Vetro”
comodino2.materiali; // ”Legno”
constructor stealing
I metodi devono essere
aggiunti sul costruttore base perché il prototipo non è visibile dai sottotipi
Prototype Chaining
Classical Inheritance
Pseudoclassical Inheritance
Prototypal Inheritance
La tecnica Pseudoclassical
Inheritance prende il meglio
dai due pattern precedenti,
utilizzando:
• prototype chaining per i
metodi
• constructor stealing per le
proprietà
function Mobile( prezzo ) {
this.materiali = [ ‘Legno’ ];
this.prezzo = prezzo;
}
Mobile.prototype.sconto = function( percentuale ) {
return this.prezzo * percentuale / 100;
};
function Comodino( prezzo, colore ) {
// eredita le proprietà
Mobile.call(this, prezzo);
this.colore = colore;
}
// eredita i metodi
Comodino.prototype = new Mobile ();
Prototype Chaining
Classical Inheritance
Pseudoclassical Inheritance
Prototypal Inheritance
function object(o) {
function F( ) { }
F.prototype = o;
return new F( );
}
var comodino1 = { materiali : [‘Legno’],
colore : ‘Bianco’ };
var comodino2 = object(comodino1);
comodino2.materiali.push(‘Vetro’);
comodino1.materiali; // “Legno”, “Vetro”
Prototypal Inheritance
(2006, Douglas Crockford)
Slide 3: http://www.flickr.com/photos/52701968@N00/14954425/
Slide 22: http://www.flickr.com/photos/msutherland1/2372319292/in/photostream/
Slide 53 : http://www.flickr.com/photos/pardeshi/1514977212
Credits Le immagini contenute in questa presentazione
hanno licenza Creative Commons
Thank You
MANUEL SCAPOLAN website: www.manuelscapolan.it twitter: manuelscapolan e-mail: [email protected]