Czy istnieje sposób na utworzenie funkcji z ciągu znaków za pomocą javascript?

108

Na przykład;

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

var fnc = aMethod(s);

Jeśli to jest ciąg, chcę funkcję o nazwie fnc. I fnc();wyskakuje ekran ostrzegawczy.

eval("alert(1);") nie rozwiązuje mojego problemu.

ymutlu
źródło

Odpowiedzi:

73

Dodałem test jsperf dla 4 różnych sposobów tworzenia funkcji z łańcucha:

  • Używanie RegExp z klasą Function

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

  • Używanie klasy funkcji z wartością „return”

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

  • Korzystanie z oficjalnego konstruktora funkcji

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

  • Korzystanie z Eval

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

http://jsben.ch/D2xTG

2 próbki wyników: wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

phnah
źródło
@KthProg Chill down;). Nie zawsze jest źle, jak ta sytuacja, jsperf w tej chwili nie działa, na szczęście dodałem zrzuty ekranu z wynikami, zanim został wyłączony, kiedy dostałem komentarz od Bulka.
phnah
@KthProg FYI to była gotowa odpowiedź wygenerowana przez system moderacji :) pojawia się w kolejce i sprawdzamy, czy nie ma z góry określonych problemów, z których jeden ma naprawić ten komentarz. Nie jest to sztywna i szybka reguła, a zauważysz, że komentarz ma formę sugestii, a nie polecenia.
Dan Smith
175

Lepszym sposobem na utworzenie funkcji z łańcucha jest użycie Function:

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

Ma to tę zaletę / wadę, że zmienne w bieżącym zakresie (jeśli nie są globalne) nie mają zastosowania do nowo skonstruowanej funkcji.

Możliwe jest również przekazywanie argumentów:

var addition = Function("a", "b", "return a + b;");
alert(addition(5, 3)); // shows '8'
Lekensteyn
źródło
5
Zgadzam się, Functionnie zanieczyszczasz zakresu lokalnego i dlatego evaloptymalizacja jest tak trudna dla silników ... Na przykładzie OP:var fnc = Function('return '+s)();
CMS
Myślę, że to prawdopodobnie powinna być akceptowana odpowiedź. Jest dużo bezpieczniejsze niż eval ().
Bryan Rayner,
Ty, mój przyjacielu, zasługujesz na wiele pochwał. Ma to tę zaletę, że tworzy obiekt funkcji, który można przypisywać do zdarzeń itp. Na przykład: element.onclick = Function ("alert ('test');");
Ryan Griggs
1
@RyanGriggs W twoim przypadku nie potrzebujesz funkcji "eval", więc lepiej jest napisać jako element.onclick = function() { alert("test"); }.
Lekensteyn
1
Masz rację z mojego przykładu. Jeśli jednak chcesz przypisać dowolne funkcje przechowywane w łańcuchach, Twoja metoda jest idealna. Właśnie to próbuję zrobić. Mam wiele funkcji przechowywanych w zmiennych łańcuchowych i chcę przypisać jedną do akcji onclick.
Ryan Griggs
38

Jesteś całkiem blisko.

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

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

//Call the function
test();

Oto działające skrzypce .

James Hill
źródło
Wiedziałem, że funkcja została zadeklarowana, ale nie mogłem odgadnąć nazwy funkcji. Wielkie dzięki.
ymutlu
4
Obowiązkowe evalostrzeżenie dla przyszłych poszukiwaczy: evalmoże otwierać luki dla hakerów: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ... ale jeśli znasz niebezpieczeństwa i potrafisz ich uniknąć, to jest to prosty sposób utwórz funkcję z łańcucha
co jeśli nie masz nazwy funkcji, ponieważ pochodzi ona z rekordu bazy danych?
Marcel Djaman
13

Tak, używanie Functionjest świetnym rozwiązaniem, ale możemy pójść trochę dalej i przygotować uniwersalny parser, który parsuje ciąg i konwertuje go na prawdziwą funkcję JavaScript ...

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;
    };
}

przykłady użycia:

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

tutaj jest jsfiddle

Mr. Pumpkin
źródło
cześć proszę o wsparcie funkcji strzałek do tej metody?
satish kumar
1
Otrzymuję ten błąd w typachcirpt „Właściwość 'parseFunction' nie istnieje w typie 'String'.”
Cegone
11

Dynamiczne nazwy funkcji w JavaScript

Za pomocą 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'

Źródło: http://marcosc.com/2012/03/dynamic-function-names-in-javascript/

Za pomocą eval

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

Za pomocą sjsClass

https://github.com/reduardo7/sjsClass

Przykład

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

var x = new newClassName();
// Next is TRUE
newClassName.name === 'newClassName'
Eduardo Cuomo
źródło
6

Ta technika może być ostatecznie równoważna metodzie eval, ale chciałem ją dodać, ponieważ może być przydatna dla niektórych.

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

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

document.body.appendChild( newCode );

Funkcjonalnie jest to podobne do dodania tego elementu <script> na końcu dokumentu, np .:

...

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

</body>
</html>
David Newberry
źródło
3

Użyj new Function()z powrotem do środka i wykonaj go natychmiast.

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

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

Fernando Carvajal
źródło
1

Przykład z dynamicznymi argumentami:

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
źródło
Dzięki. Bardzo interesujące
Дмитрий Васильев