¿Hay alguna forma de crear una función a partir de una cadena con javascript?

107

Por ejemplo;

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

var fnc = aMethod(s);

Si esta es la cadena, quiero una función que se llame fnc. Y fnc();aparece la pantalla de alerta.

eval("alert(1);") no resuelve mi problema.

ymutlu
fuente

Respuestas:

73

Agregué una prueba jsperf para 4 formas diferentes de crear una función a partir de una cadena:

  • Usando RegExp con la clase de función

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

  • Usando la clase de función con "retorno"

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

  • Usando el constructor de funciones oficial

    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 muestras de resultados: ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

phnah
fuente
@KthProg Relájate;). No siempre es malo, como esta situación, el jsperf está caído en este momento, afortunadamente agregué las capturas de pantalla de resultados antes de que cayera, cuando recibí el comentario de Bulk.
phnah
@KthProg FYI esta fue una respuesta predefinida generada por el sistema de moderación :) aparece en una cola y buscamos problemas predeterminados, uno de los cuales este comentario está diseñado para solucionar. No es una regla estricta y rápida, y notará que el comentario tiene la forma de una sugerencia, no de un comando.
Dan Smith
174

Una mejor manera de crear una función a partir de una cadena es usando Function:

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

Esto tiene como ventaja / desventaja que las variables en el alcance actual (si no es global) no se aplican a la función recién construida.

Pasar argumentos también es posible:

var addition = Function("a", "b", "return a + b;");
alert(addition(5, 3)); // shows '8'
Lekensteyn
fuente
5
De acuerdo, con Functionusted no contaminan el alcance local y es por eso que evalhace que la optimización sea tan difícil para los motores ... Con el ejemplo de OP, yo haría:var fnc = Function('return '+s)();
CMS
Creo que esta probablemente debería ser la respuesta aceptada. Es mucho más seguro que eval ().
Bryan Rayner
Tú, amigo mío, mereces muchos votos a favor. Esto tiene la ventaja de crear un objeto de función que se puede asignar a eventos, etc. Por ejemplo: element.onclick = Function ("alert ('test');");
Ryan Griggs
1
@RyanGriggs En su caso, no necesita la funcionalidad "eval", por lo que es mejor escribirla como element.onclick = function() { alert("test"); }.
Lekensteyn
1
Tienes razón por mi ejemplo. Sin embargo, si desea asignar funciones arbitrarias almacenadas en cadenas, su método es perfecto. Esto es lo que estoy tratando de hacer. Tengo varias funciones almacenadas en variables de cadena y quiero asignar una a una acción al hacer clic.
Ryan Griggs
38

Estás bastante cerca.

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

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

//Call the function
test();

Aquí hay un violín que funciona .

James Hill
fuente
Sabía que la función estaba declarada, pero no podía adivinar cómo llamar al nombre de la función. Muchas gracias.
ymutlu
4
evalAdvertencia obligatoria para futuros buscadores: evalpuede abrir lagunas para los piratas informáticos: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… pero si conoce sus peligros y puede evitarlos, esta es una forma sencilla y agradable de crear una función a partir de una cadena
¿Qué pasa si no tiene el nombre de la función ya que proviene de un registro de base de datos?
Marcel Djaman
13

Sí, usar Functiones una gran solución, pero podemos ir un poco más allá y preparar un analizador universal que analice la cadena y la convierta a una función 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;
    };
}

ejemplos 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();

aquí está jsfiddle

Sr. Calabaza
fuente
hola por favor apoye la función de flecha para este método?
sathish kumar
1
Recibo este error en typescirpt "La propiedad 'parseFunction' no existe en el tipo 'String'".
Cegone
11

Nombres de funciones dinámicas en JavaScript

Utilizando 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'

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

Utilizando eval

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

Utilizando sjsClass

https://github.com/reduardo7/sjsClass

Ejemplo

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

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

Esta técnica puede ser en última instancia equivalente al método eval, pero quería agregarlo, ya que podría ser útil para algunos.

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

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

document.body.appendChild( newCode );

Esto es funcionalmente como agregar este elemento <script> al final de su documento, por ejemplo:

...

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

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

Use el new Function()con un retorno adentro y ejecútelo inmediatamente.

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

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

Fernando Carvajal
fuente
1

Un ejemplo con 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
fuente
Gracias. Muy interesante
Дмитрий Васильев