Tutorial:What is that Scope all about2 (Italian) (Legacy)

This version of our Learning Center is unmaintained.
This article may be out-of-date or contain incorrect information.
Please visit the new Sencha Learning Center for up-to-date material.

Go to the new Sencha Learning Center

From Sencha - Learn

Jump to: navigation, search
Summary: Qualche altra curiosità sugli ambiti (scope).
Author: Michael LeComte (traduttore: Giacomo Magisano)
Published: April 29, 2008
Ext Version: 2.0+
Languages: en.png Englishcn.png Chinese it.png Italian fr.png French

Contents

Introduzione

Come con il precedente tutorial , firebug sarà molto comodo per testare i concetti mostrati qui.

Funzioni Annidate

Quando abbiamo a che fare con funzioni annidate c'è un cambio di scope non proprio intuitivo. Copiate il codice che trovate qui sotto in firebug e controllate i risultati.

var testvar = 'window property';
var o1 = {testvar:'1', fun:function(){alert('o1: '+this.testvar+'<<');}};
var o2 = {testvar:'2', fun:function(){alert('o2: '+this.testvar);}};
o1.fun();'1'
o2.fun();'2'
o1.fun.call(o2);'2'

Il primo tutorial ci aveva portato fino a qui.

var testvar = 'window property';
var o3 = {
   testvar:'3',
   testvar2:'3**',
   fun:function(){
      alert('o3: '+this.testvar);//'obj3'
      var inner = function(){
         alert('o3-inner: '+this.testvar);//'window property'
         alert('o3-inner: '+this.testvar2);//undefined
      };
      inner();
   }
};
o3.fun();

Aggiungiamo un'altra funzione, che è quasi identica a quelle precedenti se si eccettua la funzione annidata. Notate come la funzione annidata sia eseguita in un ambito differente da quella che la contiene. Ext permette di specificare l'ambito di esecuzione di una chiamata di funzione per aiutare a risolvere questo problema di ambiti (scope) nelle funzioni chiamate.

Dichiarazione di variabili

Bisogna sempre inizializzare le variabili con 'var', altrimenti creerete una variabile globale. Per esempio, nel codice che segue c'è una variabile inserita dentro una funzione che dovrebbe essere utilizzata solo localmente alla funzione ma che, in realtà, sovrascrive la sua versione globale. Usando Firebug è possibile esaminare tutte le variabili glovali ispezionando 'window' nella sezione 'DOM'. Dopo aver selezionato 'window' vedrete tutte le varaibili globali. Se all'improvviso spunta una 'k' o una 'x' o qualunque altra variabile inaspetatta saprete che avete dimenticato di assegnarle un ambito appropiato. Vediamo un esempio:

var i = 4;
var j = 5;
var k = 7;
var fn = function(){
   var i = 6;
   k = 8;//Non è preceduta da var e quindi modifica la variabile k globale!
   alert(i);//6
   alert(j);//5
   alert(k+'-1');//8-1
   x = 1;// Qui abbiamo appena creato o sovrascritto la variabile globale x
};
fn();
alert(k+'-2');//8-2 (Nota che non è 7-2)

Pretty much the same as earlier. Notice that k inside the function does not have a var in front of it, so we actually re-assign the value of the global variable instead of declare a local variable. Also notice that the alert for i finds the locally defined version first and reports that value, but for j there is no locally assigned version so it works up the scope chain until it finds the global version.


Blocchi anonimi senza ambito

Ref. Le variabili create dentro blocchi anonimi (a esempio nei cicli for e nei rami degli if) persistono fino alla fine della funzione che contiene il blocco. Per esempio, se abituati ai linguaggi con ambito strutturato ai blocchi, potreste aspettarvi che la variabile i in questo esempio valuti "123"

function foo() {
     var i=123;
     for (var i=0; i<3; i++) {
       ...
     }
     alert(i);
   }

In Javascript, il valore mostrato è "3". Questo succede perché "i" è locale alla funzione, non al blocco, e quindi la "i" usata nel for è la stessa dichiarata esternamente al ciclo. Notate che Javascript non da warning o errori se una stessa variabile viene dichiarata più volte nello stesso ambito tramite "var" .

Specificare l'ambito(scope) in Ext

Come si accennava ne precedente tutorial, Ext offre diversi modi di specificare l'ambito quando viene invocata una funzione per aiutare a risolvere il problema che si evidenzia con gli ambiti annidati. Porzioni di questa sezione vengono da questo post di dj.

Potete pensare a this come un parametro speciale (nascosto) associato ad ogni funzione. Javascript modifica this ad ogni chiamata di funzione. Lo fa seguendo una regola molto semplice: Se la funzione è un membro diretto di un oggetto allora this diventa un riferimento all'oggetto in questione. In caso contrario allora this punterà all'oggetto globale (cioè l'oggetto window nel browser). Questo spiega il comportamento della funzione annidata che abbiamo visto sopra .

una funzione che è una variabile di un'altra funzione non è membro diretto di alcun oggetto e quindi otterrà l'oggetto window come parametro this (vedi l'esempio qui sopra). Ecco un esempio, che potrete seguire copiandolo ed incollandolo in Firebug:

var obj = {
	toString:function(){ return 'in scope of obj';}, // modifichiamo l'output di console.log(this) per chiarezza
	func: function(){
		// questa funzione è membro diretto di un "oggetto"
		console.log(this); 
		var innerFunc = function(){
			// Questa funzione non è membro diretto di un oggetto, é una variabile di un'altra funzione
			console.log(this); 
		};
		innerFunc();
	}
};
obj.func(); 
// outputs "in scope of obj"
// outputs "Window something"

E' possibile cambiare il comportamento di default - possiamo assegnare esplicitamente un valore al parametro this per una chiamata a funzione. Per fare questo, però, c'è bisofno di una sintassi alternativa. Modifichiamo l'ultima istruzione "obj.func();" in:

obj.func.call(window);
// outputs "Window something"
// outputs "Window something"

Come abbiamo visto nei precedenti esempi, call chiama in realtà un'altra funzione. call è un metodo predefinito di obj.func (Come saprete, in Javascript le funzioni sono oggetti).

Indicando esplicitamente l'ambito (scope) di this possiamo modificare l'esempio per correggere il parametro this di innerFunc:

var obj = {
	toString:function(){ return 'in scope of obj';},  // modifichiamo l'output di console.log(this) per chiarezza
	func: function(){
		// questa funzione è membro diretto di un "oggetto"
		console.log(this); 
		var innerFunc = function(){
			// Questa funzione non è membro diretto di un oggetto, é una variabile di un'altra funzione
			console.log(this); 
		};
		innerFunc.call(this);
	}
};
obj.func(); 
// outputs "in scope of obj"
// outputs "in scope of obj"

Ext: Configurare lo scope

Per quanto abbiamo visto finora, le funzioni non rispettano le regole di determinazione degli ambiti (scope). Per le funzioni annidate "this" sarà un riferimento all'oggetto window del browser (come i gestori degli eventi, etc) a meno di non trovare un modo di assegnargli un riferimento diverso. *Come accennato precedentemente, Ext ha alcuni ausili (maggiori informazioni qui) che possiamo usare. Diverse classi in Ext anno una opzione di configurazione scope che possiamo usare per specificare il binding del puntatore this. Ad esempio, facciamo riferimento alle API per Ajax.request.

Ext.Ajax.request({
     scope:this
    ,success:this.populateGrid//inside populateGrid 'this' will be the scope specified in the line above
    ,failure:function(){...}.createDelegate(that)
    , ...
});

Specificando "scope: qualcosa" (dove qualcosa di solito è this), tutte le funzioni annidate da quel punto in avanti utilizzeranno l'ambito specificato. Se per qualche ragione non volessimo che la funzione che gestisce il ritorno con errore operi nell'ambito this, allora dovremo usare createDelegate im modo che l'ambito non venna assegnato a tutte le funzioni annidate.

Ext: createDelegate

createDelegate è un altro modo di associare una funzione al puntatore this. Invocando createDelegate, vi assicurerete che [NDT: nelle successive invocazioni] this sarà il parametro passato a createDelegate. Un esempio:

var obj = {
	toString:function(){ return 'in scope of obj';},  // modifichiamo l'output di console.log(this) per chiarezza
	func: function(){
		// questa funzione è membro diretto di un "oggetto"
		console.log(this); 
		var innerFunc = function(){
			// Questa funzione non è membro diretto di un oggetto, é una variabile di un'altra funzione
			console.log(this); 
		};
		innerFunc = innerFunc.createDelegate(this); // Sovrascriviamo la funzione con il delegato
		innerFunc(); // adesso possiamo utilizzare la funzione come faremmo normalmente
	}
};
obj.func(); 
// outputs "in scope of obj"
// outputs "in scope of obj"

In questo piccolo esempio tutto è particolarmente semplice e. speriamo, comprensibile. Nei programmi reali è facile perdersi, anche se lo schema è lo stesso e potrete cavarvela con le semplici regole riportate qui sopra.

createDelegate è anche utilizzabile quando si vuole settare un handler di una funzione non anonima con una serie di argomenti:

var menu = new Ext.menu.Menu(
{
   items:
   [
      text: 'Item 1',
      handler: myHandler.createDelegate(someScope, ['Item1', 'Is', 'Clicked'], true)
   ]
}
);
 
function myHandler(e, text1, text2, text3)
{
   //Posso aggiungere dati extra ad un evento esistente
   //Posso anche assegnare facilmente l'ambito di escuzione.
}

Approfondimenti

This page was last modified on 15 October 2010, at 22:53. This page has been accessed 6,517 times.