Ho una funzione es
var test = function () {alert(1);}
Come posso ottenere il corpo di questa funzione?
Suppongo che l’unico modo sia quello di analizzare il risultato del metodo test.toString (), ma esiste un altro modo? Se l’analisi è l’unico modo, quale sarà la regex per arrivare al corpo? (l’aiuto con la regex è estremamente necessario, perché non ho familiarità con loro)
IF (!!!) puoi ottenere toString()
, quindi puoi semplicemente prendere la sottostringa dal primo indexOf("{")
lastIndexOf("}")
. Quindi, qualcosa come questo “funziona” ( come visto su ideone.com ):
var test = function () {alert(1);} var entire = test.toString(); // this part may fail! var body = entire.substring(entire.indexOf("{") + 1, entire.lastIndexOf("}")); print(body); // "alert(1);"
Aggiornamento 2015
Dopo aver rivisitato lo stato di decompilazione della funzione , si può affermare che è generalmente sicuro in determinati casi d’uso e ambienti ben ponderati (es .: lavoratori Node.js con funzioni definite dall’utente).
Dovrebbe essere messo nello stesso bucket di eval , che è uno strumento potente che ha il suo posto, ma dovrebbe essere usato solo in rare occasioni. Pensaci due volte, questo è il mio unico consiglio.
Le conclusioni della nuova ricerca di Kangax :
“decompilazione delle funzioni” – un processo per ottenere la rappresentazione di una stringa di un object Function.
Generalmente, la decompilazione delle funzioni è consigliata, in quanto si tratta di una parte non standard della lingua e, di conseguenza, il codice non è interoperabile e potenzialmente sobject a errori .
@kangax su comp.lang.javascript
Se vuoi solo eseguire il corpo della funzione (ad esempio con eval
o usando l’API Worker
), puoi semplicemente aggiungere del codice per aggirare tutte le insidie dell’estrazione del corpo della funzione (che, come menzionato da altri, è un ctriggers idea in generale):
'(' + myFunction + ')()';
Sto usando questo trucco in questo JSFiddle basato su Worker
.
Ho anche scritto una libreria più completa che può:
Controlla qui il mio codice CodeBuilder
.
Si noti che gran parte del codice si occupa di assicurarsi di ottenere uno stacktrace accurato, ovunque si esegua la funzione serializzata in un momento successivo.
Questo violino dimostra una versione semplificata di quella logica:
JSON.stringify
per serializzare correttamente la funzione (che è utile quando, ad esempio, vogliamo renderla parte di un “pacchetto dati” di serializzazione più grande). eval
per scappare dalla stringa “JSON-ish” (JSON non ammette funzioni + codice, quindi dobbiamo usare eval
), e poi in un’altra eval
per recuperare l’object che volevamo. //# sourceMappingURL
(o la vecchia versione //@ sourceMappingURL
) per mostrare il giusto nome di funzione nello stacktrace. Codebuilder
usa lo stacktracejs per correggerlo. Io uso la roba di CodeBuilder
nella mia libreria RPC (ora un po ‘datata) dove puoi trovare alcuni esempi di come viene usata:
serializeInlineFunction
serializeFunction
buildFunctionCall
esempio estendendo la risposta di @polygenelubrificanti:
utilizzando: .toString()
testee:
var y = /* olo{lo} */ /* {alala} */function/* {ff} */ x/*{s}ls{ }ls*/(/*{*{*/)/* {ha-ha-ha} */ /* it's a function */ { return 'x'; // } } /* */
Con lastIndexOf
e lastIndexOf
:
function getFunctionBody(fn) { function removeCommentsFromSource(str) { return str.replace(/(?:\/\*(?:[\s\S]*?)\*\/)|(?:([\s;])+\/\/(?:.*)$)/gm, '$1'); } var s = removeCommentsFromSource( fn.toString() ); return s.substring(s.indexOf('{')+1, s.lastIndexOf('}')); }; getFunctionBody(y); /* " return 'x' " */
usato: rm commenti da js source
var fn1 = function() {}; var fn2 = function() { alert("lol!"); }; Function.prototype.empty = function() { var x = this.toString().match(/\s*function\s*\w*\s*\(.*?\)\s*{\s*}\s*;?\s*/); return x != null; }; alert(fn1.empty()); // true alert(fn2.empty()); // false
‘Solução proposta pelo Paulo Torres no grupo APDA no facebook.
Questo codice fornisce il corpo quando si usano le funzioni freccia ES6 come var testFn=(q)=>q+1;
function getFunctionBody(test){ var entire = test.toString(); // note: not safe-guarded; this part may fail like this! return entire.substring((entire.indexOf("{")+1)||(entire.indexOf("=>")+2), entire.lastIndexOf("}")!==-1?entire.lastIndexOf("}"):entire.length); } //testing/showcase code var tests = [ function () {alert(1);}, ()=>{return 1;}, q=>q+1 ]; for (var i=0;i
Inizialmente avevo inviato questo codice come risposta alla risposta accettata per i polygenelubrificanti, ma è stato rifiutato in quanto le modifiche erano considerate troppo drastiche.
Prova questo:
/\{(\s*?.*?)*?\}/g.exec(test.toString())[0]
test.toString () manterrà l’intera dichiarazione.
/ {(\ s * ? . ?) ?} / g corrisponderà a tutto tra le parentesi graffe