Perché i nomi delle mie funzioni JavaScript sono in conflitto?


97

Ho scritto il seguente script solo per vedere cosa succede quando una variabile e una funzione a cui è assegnata una funzione hanno i loro nomi in conflitto:

var f = function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

L'output che ricevo è "Io originale". Perché l'altra funzione non è stata chiamata?

Inoltre, se cambio la mia assegnazione originale in var f = new function() {, ottengo "Me original", seguito da un'eccezione TypeError object is not a function. Qualcuno può spiegare per favore?


26
@ Dean.DePue - Non c'è confusione da parte di JavaScript. Le regole per gestirle sono abbastanza chiare (e spiegate da Benjamin nella sua risposta).
Quentin

4
Curiosità, ancora il modo migliore per conoscere una lingua. :-D
Cerbrus

2
Inoltre, immagino che sia praticamente impossibile per qualcosa di così immateriale come "JavaScript" "sentirsi" confuso (o qualsiasi emozione, del
resto

2
Perché il sollevamento dovrebbe invertire l'ordine nel secondo esempio?
Cerbrus

5
Passaggi per aumentare la conoscenza di javascript: 1) Usa "use strict" 2) Usa sempre jslint o jshint 3) Cerca le cose di cui jslint o jshint si lamenta 4) Risciacqua e ripeti
steve-er-rino

Risposte:


170

Le dichiarazioni di funzione vengono sollevate (spostate all'inizio) in JavaScript. Sebbene errato in termini di ordine di analisi, il codice che hai è semanticamente lo stesso del seguente poiché le dichiarazioni di funzione sono sollevate:

function f() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Che a sua volta, ad eccezione del nome della funzione, è lo stesso di:

var f = function() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Che a sua volta, a causa del sollevamento variabile, è lo stesso di:

var f;
f = function() {
    console.log("Me duplicate.");
}
f = function() {
    console.log("Me original.");
}

f();

Il che spiega cosa stai ottenendo, stai ignorando la funzione. Più in generale, varin JavaScript sono consentite più dichiarazioni: var x = 3; var x = 5è perfettamente legale. Nel nuovo standard ECMAScript 6, le letdichiarazioni lo vietano.

Questo articolo di @kangax fa un lavoro fantastico nel demistificare le funzioni in javascript


2
Si può davvero semplificare function f()per var f = function()così tanto? Sollevamento e nomi delle funzioni sono davvero l' unica differenza?
Djechlin

6
@djechlin nel contesto di questa domanda - sì. In generale, è più sottile: vedi stackoverflow.com/questions/336859/… . Dal punto di vista del compilatore, sono diversi, ma dal punto di vista del programmatore siamo abbastanza vicini da affermarlo. Ecco perché ho aggiunto che a lungo "sebbene errato in termini di ordine di analisi, il codice che hai è semanticamente lo stesso di" invece di dire "è lo stesso di". Buon punto.
Benjamin Gruenbaum

1
@dotslash per favore non modificare la tua domanda originale e cambiarla, questo è considerato maleducato qui - inoltre, anche mescolare più domande in una è considerato maleducato qui. Puoi invece fare una nuova domanda o, se pensi che sia troppo minore, chiedere chiarimenti nei commenti (è a cosa servono comunque). Nel codice sopra, entrambe le versioni di fvengono sollevate e la "Me Original"versione viene semplicemente sollevata in un secondo momento , ciascuna viene spostata in alto ma nello stesso ordine. Vorrei solo aggiungere che in generale, non dovresti nominare più funzioni allo stesso modo :)
Benjamin Gruenbaum

5
In modalità rigorosa non puoi usare varlo stesso nome due volte nello stesso ambito.
Hoffmann

4
"Dovrebbe essere ovvio" - per te forse, ma non era ovvio per me a un certo punto, e non era ovvio per OP quando lo ha chiesto, e la denominazione, e più in generale come l'ambiente lessicale è gestito in JavaScript era uno delle cose più difficili da comprendere quando ho imparato JavaScript per la prima volta. Non sarei così veloce a insultare le persone che non lo capiscono.
Benjamin Gruenbaum

10

Se non sembra che nessuno abbia risposto alla tua domanda di follow-up, risponderò qui, anche se in genere dovresti porre domande di follow-up come domande separate.

Hai chiesto perché questo:

var f = new function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

stampa "Io originale". e poi un errore.

Quello che sta succedendo qui è che le newcause della funzione da utilizzare come un costruttore. Quindi questo è equivalente al seguente:

function myConstructor() {
    console.log("Me original.");
}
var f = new myConstructor();

function f() {
    console.log("Me duplicate.");
}

f();

E grazie alla funzione di sollevamento spiegata da Benjamin, quanto sopra è sostanzialmente equivalente a questo:

var myConstructor = function() {
    console.log("Me original.");
};
var f = function() {
    console.log("Me duplicate.");
};

f = new myConstructor();

f();

Questa espressione:

var f = new function() {
    console.log("Me original.");
}

fa sì che un nuovo oggetto venga costruito e assegnato a f, utilizzando una funzione anonima come costruttore. "Io originale." viene stampato durante l'esecuzione del costruttore. Ma l'oggetto che viene costruito non è di per sé una funzione, quindi quando questo alla fine viene eseguito:

f();

ottieni un errore, perché fnon è una funzione.


Oh, meraviglioso! Grazie mille per esserti preso la briga di rispondere! :) :)
ankush981

2

Perdonami se questo è il modo sbagliato di affrontare l'aggiunta di un punto. Non sono stato qui molto spesso e sarei lieto di ricevere indicazioni costruttive e / o critiche.

La risposta di Benjamin affronta la domanda dell'OP in modo eccellente, ma vorrei aggiungere una modifica che ci darà un giro completo del sollevamento e delle sue stranezze.

Se iniziamo il codice originale con una chiamata a f, in questo modo:

f();

var f = function() {
   console.log("Me original.");
};

function f() {
   console.log("Me duplicate.");
}

f();

L'output sarà quindi:

Me duplicate.
Me original.

Il motivo è che vare le functiondichiarazioni vengono sollevate in modi leggermente diversi.

Poiché varla dichiarazione viene spostata all'inizio dell'ambito corrente *, ma qualsiasi assegnazione non viene sollevata. Per quanto riguarda il valore della var dichiarata, è indefinito finché non viene raggiunta la riga di assegnazione originale.

Per le functiondichiarazioni , vengono issate sia la dichiarazione che la definizione. Le espressioni di funzione , come utilizzate nel var f = function() {...costrutto, non vengono sollevate.

Quindi, dopo il sollevamento, l'esecuzione è come se il codice fosse:

var f; // declares var f, but does not assign it.

// name and define function f, shadowing the variable
function f() { 
  console.log("Me duplicate.");
}

// call the currently defined function f
f(); 

// assigns the result of a function expression to the var f,
// which shadows the hoisted function definition once past this point lexically
f = function() { 
  console.log("Me original."); 
}

// calls the function referenced by the var f
f();

* Tutto l'ambito JavaScript è lessicale, o funzione, ambito, ma a quel punto sembrava confondere le cose usare la parola f.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.