JavaScript, 5 modi per chiamare una funzione

Come chiamare una funzione in Javascript? Di volta in volta mi trovo ad avere a che fare con del codice JavaScript (scritto da me!) che presenta bug causati dalla mancanza di una corretta comprensione di come le funzioni operano in JavaScript.

Per cominciare a capire come agisce JavaScript, esaminiamo cinque modi per richiamare una funzione.

Questo post è la traduzione dell’articolo JavaScript, 5 ways to call a function, di cui Sergio Pereira ne è l’autore.

Ecco come procede l’autore: creiamo prima una semplice funzione che useremo per il resto di questo post.
Questa funzione serve solo a restituire un array, con il suo valore corrente e i due argomenti forniti.


Metodo numero uno: funzione globale

Appena abbiamo imparato JavaScript, impariamo come definire le funzioni utilizzando la sintassi dell’esempio precedente: è facile da scrivere e molto facile da chiamare, tutto quello che dobbiamo fare è:

makeArray('one', 'two');
// => [ window, 'one', 'two' ]

Ma…un momento! Cosa ci fa lì l’oggetto window? Perché è il valore di ‘this’ ? Cerchiamo di capirlo insieme.

In JavaScript, e non specificamente del browser, esiste un oggetto globale/default. È come se ogni codice che scriviamo, che sembra essere “libero” all’interno dello script (cioè al di fuori di qualsiasi dichiarazione di oggetto) viene effettivamente scritto nel contesto di tale oggetto globale. Nel nostro caso, tale funzione makeArray non è solo una funzione “globale”, è un metodo dell’oggetto globale. Tornando al browser, in tale ambiente l’oggetto globale è mappato all’oggetto window. Ecco la prova:

alert( typeof window.methodThatDoesntExist );
// => undefined
alert( typeof window.makeArray);
// => function

Questo significa che possiamo chiamare la funzione precedente anche in questo modo:

window.makeArray('one', 'two');
// => [ window, 'one', 'two' ]

È un peccato che questo sia il modo più comune perché ci porta a dichiarare le nostre funzioni a livello globale. E tutti sappiamo che creare membri globali non è esattamente la migliore pratica in programmazione. Ciò è particolarmente vero in JavaScript.

Regola #1: Chiamare direttamente una funzion senza un oggetto proprietario esplicito, come myFunction(), fa sì che il valore di this corrisponda all’oggetto di default (cioè, la finestra del browser).

Metodo numero 2: metodo!

Creiamo ora un piccolo oggetto, e utilizziamo la funzione makeArray come uno dei suoi metodi. Dichiariamo l’oggetto utilizzando la notazione letterale e facciamo anche la chiamata a questo metodo:

//creiamo l'oggetto
var arrayMaker = {
	someProperty: 'some value here',
	make: makeArray
};

//invochiamo il metodo make()
arrayMaker.make('one', 'two');
// => [ arrayMaker, 'one', 'two' ]
// sintassi alternativa, utilizzando le parentesi quadre
arrayMaker['make']('one', 'two');
// => [ arrayMaker, 'one', 'two' ]

Si vede qui la differenza? Il valore di this è diventato l’oggetto stesso. Una funzione in JavaScript è un tipo di dati standard, un oggetto vero e proprio. È come se l’intera funzione assieme all’elenco di argomenti sia stato copiato e assegnato al metodo make dell’oggetto arrayMaker. È proprio come definire arrayMaker così:

var arrayMaker = {
	someProperty: 'some value here',
	make: function (arg1, arg2) {
		return [ this, arg1, arg2 ];
	}
};

Regola #2: Invocare una funzione con la sintassi del metodo di invocazione, come obj.myFunction() o obj['myFunction'](), nel caso precedente l’oggetto arrayMaker, fa coincidere il valore di this con obj

Questo aspetto è una delle principali fonti di bug nel codice per la gestione degli eventi. Diamo una occhiata a questi esempi:






Facendo clic sul primo pulsante btn1 visualizzeremo nella console “btn1” perché è una chiamata al metodo e this verrà assegnato all’oggetto proprietario (l’elemento input del pulsante.) Facendo clic su btn1, il secondo pulsante, visualizzeremo “windows” perché ButtonClicked viene chiamata direttamente (cioè, non come obj.ButtonClicked()). Succede la stessa cosa quando si assegna il gestore di eventi direttamente nel tag dell’elemento, come per btn3, il terzo pulsante. Facendo clic sul terzo pulsante succede la stessa cosa del secondo pulsante.

Questo è un altro vantaggio di usare una libreria come jQuery. Quando si definiscono i gestori di eventi, gli event handlers, in jQuery, la libreria si prenderà cura di sovrascrivere il valore del this e assicurarsi che contenga un riferimento all’elemento che era l’origine dell’evento.

//Se usiamo jQuery...
$('#btn1').click( function() {
	alert( this.id ); 
// siamo sicuri che 'this' sarà il bottone che ho cliccato
});

Come fa jQuery a sovrascrivere il valore di ‘this’? Ecco come:

Altri due modi: apply() e call()

Quanto più si imparano a usare bene funzioni in JavaScript, più ci si ritrova a far circolare le funzioni che hanno bisogno di invocare se stesse in contesti diversi.

Proprio come fa jQuery nelle funzioni del gestore di eventi, sarà spesso necessario sovrascrivere il valore di ‘this’. Ricordate che le funzioni sono oggetti in JavaScript? Le funzioni hanno metodi predefiniti, due di loro sono apply() e call(). Possiamo usarli per fare esattamente quello: sovrascrivere il valore di ‘this’.

var suv = { 
	year: 2008, 
	model: 'Dodge Bailout'
};
makeArray.apply( suv, [ 'one', 'two' ] );
// => [ suv, 'one' , 'two' ]
makeArray.call( suv,  'one', 'two' );
// => [ suv, 'one' , 'two' ]

I due metodi sono simili: Per entrambu i metodi, il primo parametro sovrascrive il ‘this’, mentre differiscono per gli argomenti successivi. Function.apply() accetta un array di valori che saranno passati come argomenti alla funzione, e Function.call() accetta gli stessi argomenti separatamente. In pratica, per la maggior parte dei casi apply() è più conveniente .

Regola #3: Se vogliamo sostituire il valore di ‘this’ senza copiare la funzione in un altro oggetto, possiamo usare ‘myFunction.apply(obj)’ o ‘myFunction.call(obj)’.

Cioè, si può fare questo:

obj2.method1.apply (obj1, [param1, param2]);

invece di:

obj1.method1 = obj2.method1; //copia
obj1.method1 (param1, param2);

E per quanto riguarda i parametri, si può vedere sopra come ho passato i parametri da applicare.

I costruttori

Senza entrare nel dettaglio della definizione dei tipi in JavaScript, dobbiamo essere consapevoli che non ci sono classi in JavaScript, e che qualsiasi tipo personalizzato necessita di una funzione di costruzione. È anche una buona idea definire i metodi del vostro tipo utilizzando l’oggetto prototipo, che è una proprietà della funzione di costruzione. Creiamo un piccolo tipo:

//Prima dichiamo il costruttore:
function ArrayMaker(arg1, arg2) {
	this.someProperty = 'whatever';
	this.theArray = [ this, arg1, arg2 ];
}
//poi dichiariamo i metodi di istanza:
ArrayMaker.prototype = {
	unMetodo: function () {
		alert( 'chiamato unMetodo');
	},
	getArray: function () {
		return this.theArray;
	}
};

var am = new ArrayMaker( 'one', 'two' );
var other = new ArrayMaker( 'first', 'second' );

am.getArray();
// => [ am, 'one' , 'two' ]

Una cosa è molto importante da notare qui è la presenza dell’operatore ‘new’ prima della chiamata della funzione. Senza di esso, la funzione sarà solo essere chiamata come ‘funzione globale’ e quelle proprietà che stiamo creando si creerebbe nell’oggetto globale window. E noi non vogliamo che sia così! Un altro problema è che, in quanto in genere non si dispone di un valore di ritorno esplicito nella funzione di costruzione, si finirà per assegnare undefined a una variabile, se si dimentica di usare ‘new’. Per queste ragioni è una buona convenzione chiamare le funzioni di costruzione iniziando con un carattere maiuscolo. Questo dovrebbe servire come promemoria per mettere il gestore ‘new’ prima della chiamata.

Con questi accorgimenti, il codice all’interno del costruttore è molto simile ad altri costruttori scritti in altri linguaggi. Il valore ‘new’ sarà il nuovo oggetto che si sta tentando di inizializzare.

Regola #4: Quando viene utilizzato come un costruttore, come new MyFunction(), il valore di ‘this’ sarà un oggetto nuovo di zecca fornite dal runtime JavaScript. Se noi non si fa il return esplicitamente di nulla da questa funzione, il ‘this’ sarà considerato il suo valore di ritorno.

Ecco cosa ho usato per questo articolo e link utili su Come chiamare una funzione JS: