Qual è la differenza nello stile di chiusura
-
08-06-2019 - |
Domanda
Esistono due stili di chiusura popolari in JavaScript.Il primo che chiamo costruttore anonimo:
new function() {
var code...
}
e il funzione eseguita in linea:
(function() {
var code...
})();
ci sono differenze di comportamento tra i due?Uno è "meglio" dell'altro?
Soluzione
Entrambi i casi eseguiranno la funzione, l'unica vera differenza è quale potrebbe essere il valore restituito dall'espressione e quale sarà il valore di "this" all'interno della funzione.
Fondamentalmente il comportamento di
new expression
È effettivamente equivalente a
var tempObject = {};
var result = expression.call(tempObject);
if (result is not an object)
result = tempObject;
Sebbene ovviamente tempObject e result siano valori transitori che non puoi mai vedere (sono dettagli di implementazione nell'interprete) e non esiste un meccanismo JS per eseguire il controllo "non è un oggetto".
In generale la "nuova funzione() { ..}" sarà più lento a causa della necessità di creare l'oggetto this per il costruttore.
Detto questo, questa non dovrebbe essere una reale differenza poiché l'allocazione degli oggetti non è lenta e non dovresti utilizzare tale codice nell'hot code (a causa del costo di creazione dell'oggetto funzione e della chiusura associata).
Modificare:una cosa che ho capito che mi è mancato è che il tempObject
otterrà expression
s prototipo, ad es.(prima di expression.call
) tempObject.__proto__ = expression.prototype
Altri suggerimenti
@Lancia:anche il primo è in esecuzione.Confrontalo con un costruttore denominato:
function Blah() {
alert('blah');
}
new Bla();
questo in realtà sta anche eseguendo il codice.Lo stesso vale per il costruttore anonimo...
Ma non era questa la domanda ;-)
Entrambi creano una chiusura eseguendo il blocco di codice.Per una questione di stile preferisco di gran lunga la seconda per un paio di motivi:
Non è immediatamente ovvio, dando un'occhiata al primo, che il codice verrà effettivamente eseguito;la linea sembra è creando una nuova funzione, anziché eseguirla come costruttore, ma non è ciò che realmente accade.Evita il codice che non fa quello che sembra!
Anche il (function(){
... })();
crea dei bei token fermalibri in modo da poter vedere immediatamente che stai entrando e uscendo da un ambito di chiusura.Ciò è utile perché avvisa il programmatore che lo legge della modifica dell'ambito ed è particolarmente utile se stai eseguendo una postelaborazione del file, ad esempio per la minimizzazione.
Bene, ho creato una pagina come questa:
<html>
<body>
<script type="text/javascript">
var a = new function() {
alert("method 1");
return "test";
};
var b = (function() {
alert("method 2");
return "test";
})();
alert(a); //a is a function
alert(b); //b is a string containing "test"
</script>
</body>
</html>
Abbastanza sorprendentemente (per me almeno) ha avvisato sia il "metodo 1" che il metodo 2".Non mi aspettavo che il "metodo 1" venisse avvisato.La differenza era quali erano i valori di a e b.a era la funzione stessa, mentre b era la stringa restituita dalla funzione.
Il secondo esempio eseguirà la funzione dopo averla creata.
modificare:questo non è proprio vero.
Sì, ci sono differenze tra i due.
Entrambe sono funzioni anonime e vengono eseguite esattamente nello stesso modo.Ma la differenza tra i due è che nel secondo caso l'ambito delle variabili è limitato alla funzione anonima stessa.Non esiste alcuna possibilità di aggiungere accidentalmente variabili all'ambito globale.
Ciò implica che utilizzando il secondo metodo, non stai ingombrando l'ambito delle variabili globali, il che è positivo poiché questi valori delle variabili globali possono interferire con alcune altre variabili globali che potresti utilizzare in qualche altra libreria o che vengono utilizzate in una libreria di terze parti .
Esempio:
<html>
<body>
<script type="text/javascript">
new function() {
a = "Hello";
alert(a + " Inside Function");
};
alert(a + " Outside Function");
(function() {
var b = "World";
alert(b + " Inside Function");
})();
alert(b + " Outside Function");
</script>
</body>
</html>
Nel codice sopra l'output è qualcosa del tipo:
Funzione Hello Inside
Ciao funzione esterna
Funzione Mondo Interno
...quindi, ricevi un errore poiché "b" non è definito all'esterno della funzione!
Quindi credo che il secondo metodo sia migliore...più sicuro!