2016-07-15 105 views
6

Come tutti sappiamo, siamo in grado di definire funzioni con e senza un nome:Set nome della funzione dopo aver definito lo

var/let/const foo = function() {} 
function bar() {} 

Dove foo 's funzione non ha un proprio nome, ma bar fa.

console.log(foo.name) --> '' 
console.log(bar.name) --> 'bar' 

È possibile definire il nome di una funzione dopo averla definita?
Così facendo qualcosa rende console.log(foo.name) ritorno qualcosa di diverso ''

+0

Hai provato 'foo.name = 'foo';'? – jonhopkins

+6

'Object.defineProperty (foo," name ", {value:" foo "})' –

+1

@jonhopkins L'hai * provato *? : -3 – deceze

risposta

5

As we all know...

var/let/const foo = function() {} 

...

foo's function hasn't got its own name...

A partire dal ES2015 +, la funzione di foo ha in effetti un nome: foo. ES2015 ha aggiunto molti punti in cui le funzioni ottengono i nomi anche se definiti con espressioni di funzione "anonime". Questo include semplici incarichi come quello, assegnazioni a proprietà (compresi i nomi di proprietà calcolati) e altri simili. Cerca le specifiche per "SetFunctionName" per i dettagli.

Ma, come squint sottolinea nel a comment, in ES2015 +, il nome di una funzione può essere impostata tramite Object.defineProperty:

Object.defineProperty(foo, "name", {value: "aNameForFoo"}); 

Non si può semplicemente assegnare al foo.name perché name è definito come non-scrivibile. Ma è configurabile (è possibile scambiare la proprietà con una nuova), quindi funziona sopra. (I dettagli su come è definito in the spec.)

Nota che Function#name è nuova come di ES2015, e come era una caratteristica molto minore, è stato una delle priorità più basse per gli sviluppatori del motore JavaScript. Credo che Chrome   51 sia stato il primo browser con un motore JavaScript che implementa correttamente le specifiche. Questo cambierà rapidamente in questa fase.

Ma, come Assimilater punti fuori, se si desidera solo la funzione creata con l'espressione di avere un nome e tu non ne hai bisogno definito in fase di esecuzione, basta usare un chiamato funzione di espressione (NPC):

var foo = function bar() { 
    // Note ------^^^^ 
} 
//console.log(bar); // Would throw a ReferenceError, `bar` is not defined in this scope 
console.log(foo.name); // "bar" 

Gli NFE sono in circolazione da sempre, molto prima dell'ES2015. In alcuni motori erano problematici (molto presto Safari, IE8 e precedenti), ma i motori moderni (IE9 +) li gestivano correttamente.

Ecco un esempio di:

  • Il nome "dedotto"
  • Il nome aggiornato
  • Un nome assegnato da un NPC

// "Inferred" name is foo 
 
var foo = function() { 
 
    throw new Error(); 
 
}; 
 
try { 
 
    foo(); 
 
} catch (e) { 
 
    console.log(e.stack); 
 
} 
 

 
// Change it to "updatedName" 
 
Object.defineProperty(foo, "name", { 
 
    value: "updatedName" 
 
}); 
 
try { 
 
    foo(); 
 
} catch (e) { 
 
    console.log(e.stack); 
 
} 
 

 
// Example of NFE 
 
var foo2 = function bar() { 
 
    // Note --------^^^^ 
 
    console.log("bar is defined within the function: " + bar.name); 
 
    throw new Error(); 
 
}; 
 
//console.log(bar);  // Would throw an error, `bar is not 
 
// defined here 
 
console.log(foo2.name); // "bar" 
 
try { 
 
    foo2(); 
 
} catch (e) { 
 
    console.log(e.stack); 
 
}

Su un browser che implementa ES2015 di Function#name correttamente (molto pochi lo fanno partire da questa scrittura, Chrome   51 essendo uno dei primi), e che implementa Error#stack, che le uscite:

 
Error 
    at foo (http://stacksnippets.net/js:15:9) 
    at http://stacksnippets.net/js:18:3 
Error 
    at updatedName (http://stacksnippets.net/js:15:9) 
    at http://stacksnippets.net/js:28:3 
bar 
bar is defined within the function: bar 
Error 
    at bar (http://stacksnippets.net/js:37:9) 
    at http://stacksnippets.net/js:43:3 

Una nota su "inferito "nomi per funzioni definite da espressioni anonime. Considerare:

let Nifty = { 
 
    stuff: { 
 
    here: { 
 
     foo: function() { throw new Error(); } 
 
    } 
 
    } 
 
}; 
 
let f = Nifty.stuff.here.foo; 
 
console.log(f.name); // "foo"

Si potrebbe ragionevolmente pensare che la funzione finirebbe per essere Nifty.stuff.here.foo, o foo. È il secondo. (Questo nome di inferenza "ES2015" è venuto fuori dagli sforzi di Google e Mozilla per fornire nomi utili per le funzioni nei loro strumenti di sviluppo.In uno stadio, uno o l'altro   — Ho dimenticato quale   — utilizzava il nome completo, ma sembra non sono stati popolari)


Forse degno di nota:. è possibile scegliere un nome runtime quando sua creazione, come di ES2015, tramite un nome di proprietà computerizzata:

var name = "function" + Math.floor(Math.random() * 10000); 
 
var obj = {[name]: function() { throw new Error(); }}; 
 
var foo = obj[name]; 
 
try { 
 
    foo(); 
 
} catch (e) { 
 
    console.log(e.stack); 
 
}

uscita su un browser che supporti questa roba:

 
Error 
    at function3608 (http://stacksnippets.net/js:14:39) 
    at http://stacksnippets.net/js:17:3 

Ma ora che so che si può fare la Object.defineProperty trucco, beh, che sembra meno intelligente. :-)

+0

Non sono chiaro su quale sarà l'output del frammento di codice. Non viene visualizzato alcun nome nella traccia quando lo eseguo. –

+0

@squint: lo aggiusterò. Molto, molto pochi motori JvaScript implementano questa roba correttamente ancora. Chrome 51 (versioni precedenti di Chrome no). Comprensibilmente, c'erano priorità più elevate per la conformità ES2015 rispetto a "Nome funzione #". :-) –

+0

Per quanto riguarda ES2015 +, cosa succede se la funzione è stata definita come una proprietà dell'oggetto anziché la variabile globale. Ad esempio '$ .myPlugin.fn.foo = function() {}' il nome sarebbe '' $ .myPlugin.fn.foo "'? – Assimilater

Problemi correlati