2015-10-04 17 views
12

C'è un modo per recuperare il valore di argomento predefinito di una funzione in JavaScript?Ottieni il valore predefinito della funzione?

function foo(x = 5) { 
    // things I do not control 
} 

C'è un modo per ottenere il valore di default di x qui? In modo ottimale, qualcosa come:

getDefaultValues(foo); // {"x": 5} 

Nota che toString ing la funzione non avrebbe funzionato come si spezzerebbe il default che non sono costanti.

+0

[Python] (http://stackoverflow.com/questions/12627118/get-a-function-arguments-default-value), [Ruby] (http://ruby-doc.org/core-2.2. 0/Method.html # method-i-parameters) e [C#] (https://msdn.microsoft.com/en-us/library/system.reflection.parameterinfo.defaultvalue (v = vs.110) .aspx) tutto questo a proposito. –

+1

controllare questo http://stackoverflow.com/questions/894860/set-a-default-parameter-value-for-a-javascript-function –

+5

@OrBachar Questa è la domanda del parametro_set_predefinito, qui OP desidera ** ottieni ** valore del parametro predefinito dall'esterno della funzione senza chiamarlo. – Tushar

risposta

1

mi piacerebbe affrontare il problema estraendo i parametri da una versione stringa della funzione: metodo di estrazione

// making x=3 into {x: 3} 
function serialize(args) { 
    var obj = {}; 
    var noWhiteSpace = new RegExp(" ", "g"); 
    args = args.split(","); 
    args.forEach(function(arg) { 
    arg = arg.split("="); 
    var key = arg[0].replace(noWhiteSpace, ""); 
    obj[key] = arg[1]; 
    }); 
    return obj; 
    } 

function foo(x=5, y=7, z='foo') {} 

// converting the function into a string 
var fn = foo.toString(); 

// magic regex to extract the arguments 
var args = /\(\s*([^)]+?)\s*\)/.exec(fn); 

//getting the object 
var argMap = serialize(args[1]); // {x: "5", y: "7", z: "'foo'"} 

argomento è stato preso da qui: Regular Expression to get parameter list from function definition

applausi!

PS. come puoi vedere, trasforma gli interi in stringhe, che a volte possono essere fastidiosi. assicurati di conoscere in anticipo il tipo di input o assicurati che non sia importante.

+0

Questo non funziona se la funzione è associata a qualcosa di diverso da un valore letterale. Ad esempio 'let y = 5; funzione pippo (x = y) {}; '. Diversamente dalle soluzioni Python Ruby o C#. –

+0

@BenjaminGruenbaum Questo non funzionerà neanche in C#. deve essere costante nel tempo della compilazione.http: //i.imgur.com/WWDwPnC.png –

+0

a seconda della situazione, è possibile toccarlo usando qualcosa come obj [key] = eval (arg [1]) || arg [1]; nella serializzazione fn. sì, eval .. ma questa è in realtà una soluzione ragionevole – silicakes

3

Dal momento che non abbiamo riflessione classica in JS, come si può trovare su C#, Ruby, ecc, dobbiamo fare affidamento su uno dei miei strumenti preferiti, le espressioni regolari, per fare questo lavoro per noi:

let b = "foo"; 
function fn (x = 10, /* woah */ y = 20, z, a = b) { /* ... */ } 

fn.toString() 
    .match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1] // Get the parameters declaration between the parenthesis 
    .replace(/(\/\*[\s\S]*?\*\/)/mg,'')    // Get rid of comments 
    .split(',') 
    .reduce(function (parameters, param) {   // Convert it into an object 
    param = param.match(/([_$a-zA-Z][^=]*)(?:=([^=]+))?/); // Split parameter name from value 
    parameters[param[1].trim()] = eval(param[2]); // Eval each default value, to get strings, variable refs, etc. 

    return parameters; 
    }, {}); 

// Object { x: 10, y: 20, z: undefined, a: "foo" } 

Se si intende utilizzare questo, è sufficiente assicurarsi di memorizzare le regex per le prestazioni.

Grazie a bubersson per suggerimenti sui primi due regexs

+2

Si interrompe se l'ambiente non è uguale: 'function foo() {var x = 5; barra delle funzioni di ritorno (y = x) {}}; var bar = pippo(); ' –

+0

Non vero. Si potrebbe ancora usare '.toString()' su quella funzione restituita. – pilau

+2

No, perché dovresti valutarlo, e ciò causerebbe un effetto collaterale. Ad esempio 'function foo() {var x = prompt (" Please enter a number "); funzione di ritorno (y = x) {}; } var bar = foo() '- come si estrae il valore' y' è legato a qui? –

1

Come afferma trattasi utilizzando toString è una soluzione limitata. Produrrà un risultato che potrebbe essere qualsiasi cosa, da un valore letterale a una chiamata di metodo. Tuttavia, questo è il caso del linguaggio stesso: consente tali dichiarazioni. Convertire tutto ciò che non è un valore letterale in un valore è un'ipotesi euristica nel migliore dei casi. Considerare i seguenti 2 frammenti di codice:

let def; 
function withDef(v = def) { 
    console.log(v); 
} 

getDefaultValues(withDef); // undefined, or unknown? 

def = prompt('Default value'); 
withDef(); 
function wrap() { 
    return (new Function(prompt('Function body')))(); 
    // mutate some other value(s) --> side effects 
} 

function withDef(v = wrap()) { 
    console.log(v); 
} 
withDef(); 
getDefaultValues(withDef); // unknown? 

Mentre il primo esempio potrebbe essere valutato (ricorsivamente se necessario) per estrarre undefined e successivamente su qualsiasi altro valore, la seconda è veramente definito come valore predefinito è non deterministico. Ovviamente è possibile sostituire prompt() con qualsiasi altro ingresso/generatore casuale.

Quindi la risposta migliore è quella che hai già. Utilizzare toString e, se lo si desidera, eval() ciò che si estrae, ma avrà effetti collaterali.

0

C'è un modo per ottenere il valore predefinito di x qui?

No, non v'è alcuna funzione di riflessione incorporata per fare queste cose, ed è del tutto impossibile in ogni caso dato come parametri di default sono stati progettati in JavaScript.

Si noti che la funzione toString() non funzionerebbe in quanto si interromperà su valori predefiniti che non sono costanti.

Infatti. L'unico modo per scoprirlo è chiamare la funzione, poiché i valori predefiniti possono dipendere dalla chiamata. Basti pensare

function(x, y=x) { … } 

e cercare di ottenere la rappresentazione ragionevole per y s valore predefinito.

In altre lingue che si è in grado di accedere ai valori di default o perché sono costante (valutati in sede di definizione), o la loro riflessione consente di abbattere le espressioni e valutare nel contesto sono stati definiti in.

In Contrasto, JS valuta le espressioni di inizializzazione dei parametri su ogni chiamata della funzione (se necessario) - dai un'occhiata a How does this work in default parameters? per i dettagli. E queste espressioni sono, come se fossero parte del corpo delle funzioni, non accessibili a livello di programmazione, così come tutti i valori a cui si riferiscono sono nascosti nell'ambito della chiusura privata della funzione.
Se si dispone di un'API di riflessione che consente l'accesso ai valori di chiusura (come l'API di debug del motore), è possibile accedere ai valori predefiniti.

È impossibile distinguere function(x, y=x) {…} da function(x) { var y=x; …} dalle loro proprietà pubbliche o dal loro comportamento, l'unico modo è .toString(). E tu non vuoi fare affidamento su quello di solito.

+0

"è del tutto impossibile dato come i parametri di default sono progettati in JavaScript." - Beh, quella parte è sbagliata. Il tuo 'Reflect.getDefaultParameterValues' potrebbe restituire getter per i parametri. –

+0

Intendi dire che dovrebbe restituire un oggetto che dopo la valutazione esegue gli effetti collaterali (l'espressione di inizializzazione predefinita) e poi genera un errore come "' x non è definito' "? Ciò è altrettanto impossibile quanto ottenere valori di chiusura da un oggetto funzione di chiusura: è pensabile, ma non progettato per essere. – Bergi

Problemi correlati