มีวิธีสร้างฟังก์ชันจากสตริงด้วยจาวาสคริปต์หรือไม่?


108

ตัวอย่างเช่น;

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

var fnc = aMethod(s);

ถ้านี่คือสตริงฉันต้องการฟังก์ชันที่เรียกว่า fnc และfnc();ปรากฏหน้าจอแจ้งเตือน

eval("alert(1);") ไม่สามารถแก้ปัญหาของฉันได้

คำตอบ:


73

ฉันเพิ่มการทดสอบ jsperf สำหรับ 4 วิธีในการสร้างฟังก์ชันจากสตริง:

  • ใช้ RegExp กับคลาสฟังก์ชัน

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

  • การใช้ฟังก์ชันคลาสกับ "return"

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

  • ใช้ตัวสร้างฟังก์ชันอย่างเป็นทางการ

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

  • ใช้ Eval

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

http://jsben.ch/D2xTG

2 ตัวอย่างผลลัพธ์: ป้อนคำอธิบายภาพที่นี่ ป้อนคำอธิบายภาพที่นี่


@KthProg Chill down;). มันไม่ได้แย่เสมอไปเช่นสถานการณ์นี้ jsperf หยุดทำงานในขณะนี้โชคดีที่ฉันเพิ่มภาพหน้าจอผลลัพธ์ก่อนที่มันจะหยุดทำงานเมื่อฉันได้รับความคิดเห็นจาก Bulk
phnah

@KthProg FYI นี่เป็นการตอบกลับสำเร็จรูปที่สร้างโดยระบบการกลั่นกรอง :) จะปรากฏขึ้นในคิวและเราตรวจสอบปัญหาที่กำหนดไว้ล่วงหน้าซึ่งหนึ่งในความคิดเห็นนี้ได้รับการออกแบบมาเพื่อแก้ไข ไม่ใช่กฎที่ยากและรวดเร็วและคุณจะสังเกตเห็นว่าความคิดเห็นนั้นอยู่ในรูปแบบของคำแนะนำไม่ใช่คำสั่ง
Dan Smith

175

วิธีที่ดีกว่าในการสร้างฟังก์ชันจากสตริงคือการใช้Function:

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

สิ่งนี้มีข้อดี / ข้อเสียที่ตัวแปรในขอบเขตปัจจุบัน (ถ้าไม่ใช่ทั่วโลก) ไม่นำไปใช้กับฟังก์ชันที่สร้างขึ้นใหม่

สามารถส่งผ่านข้อโต้แย้งได้เช่นกัน:

var addition = Function("a", "b", "return a + b;");
alert(addition(5, 3)); // shows '8'

5
เห็นด้วยกับFunctionคุณไม่ก่อให้เกิดมลพิษในขอบเขตท้องถิ่นและนี่คือสาเหตุที่evalทำให้การเพิ่มประสิทธิภาพเป็นเรื่องยากสำหรับเครื่องยนต์ ... ด้วยตัวอย่าง OP ฉันจะ:var fnc = Function('return '+s)();
Christian C. Salvadó

ฉันคิดว่านี่น่าจะเป็นคำตอบที่ได้รับการยอมรับ ปลอดภัยกว่า eval () มาก
Bryan Rayner

คุณเพื่อนของฉันสมควรได้รับคะแนนโหวตมากมาย สิ่งนี้มีข้อดีในการสร้างวัตถุฟังก์ชันซึ่งสามารถกำหนดให้กับเหตุการณ์ ฯลฯ ตัวอย่างเช่น element.onclick = Function ("alert ('test');");
Ryan Griggs

1
@RyanGriggs ในกรณีของคุณคุณไม่ต้องการฟังก์ชัน "eval" ดังนั้นจึงควรเขียนเป็นelement.onclick = function() { alert("test"); }ไฟล์.
Lekensteyn

1
คุณถูกต้องจากตัวอย่างของฉัน อย่างไรก็ตามหากคุณต้องการกำหนดฟังก์ชันตามอำเภอใจที่เก็บไว้ในสตริงวิธีของคุณก็สมบูรณ์แบบ นี่คือสิ่งที่ฉันพยายามทำจริงๆ ฉันมีหลายฟังก์ชันที่เก็บไว้ในตัวแปรสตริงและต้องการกำหนดหนึ่งฟังก์ชันให้กับการดำเนินการแบบ onclick
Ryan Griggs

38

คุณค่อนข้างใกล้

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

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

//Call the function
test();

นี่คือซอที่ใช้งานได้


ฉันรู้ว่ามีการประกาศฟังก์ชัน แต่เดาไม่ได้ว่าจะเรียกชื่อฟังก์ชัน ขอบคุณมาก.
ymutlu

4
evalคำเตือนบังคับสำหรับผู้ค้นหาในอนาคต: evalสามารถเปิดช่องโหว่สำหรับแฮ็กเกอร์: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…แต่ถ้าคุณรู้ถึงอันตรายและสามารถหลีกเลี่ยงได้นี่เป็นวิธีง่ายๆที่ดีในการ สร้างฟังก์ชันจากสตริง

จะเกิดอะไรขึ้นถ้าคุณไม่มีชื่อของฟังก์ชันเนื่องจากมาจากบันทึกฐานข้อมูล?
Marcel Djaman

13

ใช่การใช้Functionเป็นวิธีแก้ปัญหาที่ยอดเยี่ยม แต่เราสามารถไปได้ไกลกว่านั้นและเตรียมตัวแยกวิเคราะห์สากลที่แยกวิเคราะห์สตริงและแปลงเป็นฟังก์ชัน 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;
    };
}

ตัวอย่างการใช้งาน:

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

นี่คือ jsfiddle


สวัสดีโปรดสนับสนุนฟังก์ชั่นลูกศรรองรับวิธีนี้หรือไม่?
sathish kumar

1
ฉันได้รับข้อผิดพลาดนี้ใน typescirpt "Property 'parseFunction' ไม่มีอยู่ในประเภท 'String'
Cegone

11

ชื่อฟังก์ชันไดนามิกใน JavaScript

การใช้ 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'

ที่มา: http://marcosc.com/2012/03/dynamic-function-names-in-javascript/

การใช้ eval

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

การใช้ sjsClass

https://github.com/reduardo7/sjsClass

ตัวอย่าง

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

var x = new newClassName();
// Next is TRUE
newClassName.name === 'newClassName'

6

ในที่สุดเทคนิคนี้อาจเทียบเท่ากับวิธีการ eval แต่ฉันต้องการเพิ่มเพราะอาจมีประโยชน์สำหรับบางคน

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

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

document.body.appendChild( newCode );

นี่เป็นหน้าที่เหมือนกับการเพิ่มองค์ประกอบ <script> นี้ที่ส่วนท้ายของเอกสารของคุณเช่น:

...

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

</body>
</html>


1

ตัวอย่างที่มีอาร์กิวเมนต์แบบไดนามิก:

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]))

ขอบคุณ. น่าสนใจมาก
ДмитрийВасильев
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.