Existe uma maneira de criar uma função a partir de uma string com javascript?

107

Por exemplo;

var s = "function test(){
  alert(1);
}";

var fnc = aMethod(s);

Se esta for a string, quero uma função chamada fnc. E fnc();aparece a tela de alerta.

eval("alert(1);") não resolve meu problema.

ymutlu
fonte

Respostas:

73

Eu adicionei um teste jsperf para 4 maneiras diferentes de criar uma função a partir de string:

  • Usando RegExp com a classe Function

    var func = "function (a, b) { return a + b; }".parseFunction();

  • Usando a classe Function com "return"

    var func = new Function("return " + "function (a, b) { return a + b; }")();

  • Usando o construtor oficial de função

    var func = new Function("a", "b", "return a + b;");

  • Usando Eval

    eval("var func = function (a, b) { return a + b; };");

http://jsben.ch/D2xTG

2 amostras de resultados: insira a descrição da imagem aqui insira a descrição da imagem aqui

phnah
fonte
@KthProg Chill down;). Nem sempre é ruim, como esta situação, o jsperf está fora do ar no momento, felizmente adicionei as capturas de tela do resultado antes de ele cair, quando recebi o comentário do Bulk.
phnah
@KthProg FYI, esta foi uma resposta pronta gerada pelo sistema de moderação :) ela aparece em uma fila e verificamos se há problemas pré-determinados, um dos quais este comentário foi projetado para corrigir. Não é uma regra rígida e rápida, e você notará que o comentário está na forma de uma sugestão, não de um comando.
Dan Smith
174

A melhor maneira de criar uma função a partir de uma string é usando Function:

var fn = Function("alert('hello there')");
fn();

Isso tem como vantagem / desvantagem que as variáveis ​​no escopo atual (se não globais) não se aplicam à função recém-construída.

Também é possível passar argumentos:

var addition = Function("a", "b", "return a + b;");
alert(addition(5, 3)); // shows '8'
Lekensteyn
fonte
5
Concordo, com Functionvocê não polui o escopo local e é por isso que evaltorna a otimização tão difícil para os engines ... Com o exemplo de OP, eu:var fnc = Function('return '+s)();
CMS
Acho que essa provavelmente deveria ser a resposta aceita. É muito mais seguro do que eval ().
Bryan Rayner
Você, meu amigo, merece muitos votos positivos. Isso tem a vantagem de criar um objeto de função que pode ser atribuído a eventos, etc. Por exemplo: element.onclick = Function ("alert ('test');");
Ryan Griggs
1
@RyanGriggs No seu caso, você não precisa da funcionalidade "eval", por isso é melhor escrever como element.onclick = function() { alert("test"); }.
Lekensteyn
1
Você está correto no meu exemplo. No entanto, se você quiser atribuir funções arbitrárias armazenadas em strings, seu método é perfeito. É isso que estou tentando fazer. Tenho várias funções armazenadas em variáveis ​​de string e quero atribuir uma a uma ação onclick.
Ryan Griggs
38

Você está bem perto.

//Create string representation of function
var s = "function test(){  alert(1); }";

//"Register" the function
eval(s);

//Call the function
test();

Aqui está um violino funcionando .

James Hill
fonte
Eu sabia que a função foi declarada, mas não consegui adivinhar como chamar o nome da função. Muito obrigado.
ymutlu
4
evalAviso obrigatório para futuros pesquisadores: evalpode abrir brechas para hackers: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… mas se você conhece seus perigos e pode evitá-los, então esta é uma maneira simples e agradável de criar uma função a partir de uma string
e se você não tiver o nome da função, pois ela veio de um registro de banco de dados?
Marcel Djaman
13

Sim, usar Functioné uma ótima solução, mas podemos ir um pouco além e preparar um analisador universal que analisa a string e a converte em uma função JavaScript real ...

if (typeof String.prototype.parseFunction != 'function') {
    String.prototype.parseFunction = function () {
        var funcReg = /function *\(([^()]*)\)[ \n\t]*{(.*)}/gmi;
        var match = funcReg.exec(this.replace(/\n/g, ' '));

        if(match) {
            return new Function(match[1].split(','), match[2]);
        }

        return null;
    };
}

exemplos de uso:

var func = 'function (a, b) { return a + b; }'.parseFunction();
alert(func(3,4));

func = 'function (a, b) { alert("Hello from function initiated from string!"); }'.parseFunction();
func();

aqui está jsfiddle

Senhor abóbora
fonte
oi, por favor, suporte a função de suporte de seta para este método?
sathish kumar de
1
Estou recebendo este erro no typescirpt "Propriedade 'parseFunction' não existe no tipo 'String'."
Cegone de
11

Nomes de funções dinâmicas em JavaScript

Usando Function

var name = "foo";
// Implement it
var func = new Function("return function " + name + "(){ alert('hi there!'); };")();
// Test it
func();
// Next is TRUE
func.name === 'foo'

Fonte: http://marcosc.com/2012/03/dynamic-function-names-in-javascript/

Usando eval

var name = "foo";
// Implement it
eval("function " + name + "() { alert('Foo'); };");
// Test it
foo();
// Next is TRUE
foo.name === 'foo'

Usando sjsClass

https://github.com/reduardo7/sjsClass

Exemplo

Class.extend('newClassName', {
    __constructor: function() {
        // ...
    }
});

var x = new newClassName();
// Next is TRUE
newClassName.name === 'newClassName'
Eduardo cuomo
fonte
6

Essa técnica pode ser equivalente ao método eval, mas eu queria adicioná-lo, pois pode ser útil para alguns.

var newCode = document.createElement("script");

newCode.text = "function newFun( a, b ) { return a + b; }";

document.body.appendChild( newCode );

Isso é funcionalmente como adicionar este elemento <script> ao final do seu documento, por exemplo:

...

<script type="text/javascript">
function newFun( a, b ) { return a + b; }
</script>

</body>
</html>
David Newberry
fonte
3

Use o new Function()com um retorno dentro e execute-o imediatamente.

var s = `function test(){
  alert(1);
}`;

var new_fn = new Function("return " + s)()
console.log(new_fn)
new_fn()

Fernando Carvajal
fonte
1

Um exemplo com argumentos dinâmicos:

let args = {a:1, b:2}
  , fnString = 'return a + b;';

let fn = Function.apply(Function, Object.keys(args).concat(fnString));

let result = fn.apply(fn, Object.keys(args).map(key=>args[key]))
Brunettdan
fonte
Obrigado. Muito interessante
Дмитрий Васильев