eval () และ Function ใหม่ () เหมือนกันหรือไม่?


93

ฟังก์ชั่นทั้งสองนี้ทำสิ่งเดียวกันเบื้องหลังหรือไม่? (ในฟังก์ชันคำสั่งเดียว)

var evaluate = function(string) {
    return eval('(' + string + ')');
}

var func = function(string) {
    return (new Function( 'return (' + string + ')' )());
}

console.log(evaluate('2 + 1'));
console.log(func('2 + 1'));

3
อันที่จริงใช้ใน jQuery 1.7.2 บรรทัด 573 แม้ว่าจะมี "การตรวจสอบความปลอดภัยบางอย่าง"
ก็ตาม

2
เหตุใดจึงมีการnewพิจารณาในการอภิปรายนี้ Functionโดยปริยาย a function object. การยกเว้นnewจะไม่เปลี่ยนรหัสเลย นี่คือ jsfiddle ที่แสดงให้เห็นว่า: jsfiddle.net/PcfG8
Travis J

คำตอบ:


118

ไม่พวกเขาไม่เหมือนกัน

  • eval() ประเมินสตริงเป็นนิพจน์ JavaScript ภายในขอบเขตการดำเนินการปัจจุบันและสามารถเข้าถึงตัวแปรโลคัล
  • new Function()แยกวิเคราะห์โค้ด JavaScript ที่เก็บไว้ในสตริงเป็นอ็อบเจ็กต์ฟังก์ชันซึ่งสามารถเรียกใช้ได้ ไม่สามารถเข้าถึงตัวแปรภายในได้เนื่องจากโค้ดทำงานในขอบเขตที่แยกต่างหาก

พิจารณารหัสนี้:

function test1() {
    var a = 11;
    eval('(a = 22)');
    alert(a);            // alerts 22
}

หากnew Function('return (a = 22);')()ถูกใช้ตัวแปรโลคัลaจะคงค่าไว้ อย่างไรก็ตามโปรแกรมเมอร์ JavaScript บางคนเช่นดักลาสคร็อกฟอร์ดเชื่อว่าไม่ควรใช้ทั้งสองอย่างเว้นแต่ว่าจำเป็นจริงๆและการหลีกเลี่ยง / ใช้ตัวFunctionสร้างบนข้อมูลที่ไม่น่าเชื่อถือนั้นไม่ปลอดภัยและไม่ฉลาด


11
"ไม่ควรใช้" เป็นคำกล่าวกว้าง ๆ อย่างไรก็ตามไม่ควรใช้เมื่ออินพุตใด ๆ อยู่นอกเหนือการควบคุมของคุณ ต้องบอกว่าฉันไม่จำเป็นต้องใช้มันเลยยกเว้นการแยกวิเคราะห์ JSON แต่ฉันได้ตอบคำถามที่นี่ซึ่งดูเหมือนเป็นการใช้งานที่ถูกต้องมันเกี่ยวข้องกับบางอย่างเช่นการอนุญาตให้ผู้ดูแลระบบสร้างฟังก์ชันเทมเพลตที่ผู้ใช้ปลายทางสามารถใช้ได้ ในกรณีนี้ผู้ดูแลระบบสร้างอินพุตดังนั้นจึงเป็นที่ยอมรับ
Juan Mendes

3
@ Juan: ประเด็นของ Crockford คือการประเมินมักถูกนำไปใช้ในทางที่ผิด (ขึ้นอยู่กับมุมมองของคุณ) ดังนั้นจึงควรแยกออกจาก JavaScript "แนวทางปฏิบัติที่ดีที่สุด" อย่างไรก็ตามฉันยอมรับว่ามีประโยชน์สำหรับการใช้งานเช่น JSON หรือนิพจน์ทางคณิตศาสตร์ในการแยกวิเคราะห์เมื่ออินพุตได้รับการตรวจสอบความถูกต้องเป็นครั้งแรก แม้แต่ Crockford ก็ใช้ในไลบรารี JSON json2.js
PleaseStand

นี่หมายความว่าnew Function(code)เหมือนกับeval('(function(){'+code+'})()')หรือเป็นบริบทการดำเนินการใหม่ทั้งหมดหรือไม่?
George Mauer

5
@GeorgeMauer: ฉันคิดว่าคุณหมายถึงeval('(function(){'+code+'})')ซึ่งจะส่งคืนวัตถุฟังก์ชัน ตามที่ MDNกล่าวว่า "ฟังก์ชันที่สร้างด้วยตัวสร้างฟังก์ชันไม่ได้สร้างการปิดให้กับบริบทการสร้างของพวกเขาซึ่งจะทำงานในบริบทของหน้าต่างเสมอ (เว้นแต่ว่าเนื้อหาของฟังก์ชันจะขึ้นต้นด้วยคำสั่ง" ใช้อย่างเข้มงวด "ซึ่งในกรณีนี้บริบทไม่ได้กำหนดไว้) .”
PleaseStand

6

ไม่

ในการอัปเดตของคุณการโทรไปยังevaluateและfuncให้ผลลัพธ์เดียวกัน แต่พวกเขาไม่ได้ "ทำสิ่งเดียวกันเบื้องหลัง" อย่างแน่นอน funcฟังก์ชั่นสร้างฟังก์ชั่นใหม่ แต่แล้วทันทีรันในขณะที่evaluateฟังก์ชั่นเพียงแค่รันโค้ดบนจุด

จากคำถามเดิมที่ว่า

var evaluate = function(string) {
    return eval(string);
}
var func = function(string) {
    return (new Function( 'return (' + string + ')' )());
}

สิ่งเหล่านี้จะให้ผลลัพธ์ที่แตกต่างกันมาก:

evaluate('0) + (4');
func('0) + (4');

ไม่ว่าในกรณีใดทั้งสองเป็นการปฏิบัติที่ไม่ดีในทุกกรณี แต่เป็นกรณีพิเศษที่สุด
palswim

8
คุณทำตัวอย่างของคุณตามการใช้งานของเขา หากเขาดำเนินการประเมินreturn eval('(' + string + ')');แล้วก็จะได้ผลลัพธ์เช่นเดียวกัน
Juan Mendes

@ จวนไม่ได้คิด แต่จะใช่ แก้ไขคำถาม
qwertymk

6

new Functionสร้างฟังก์ชันที่สามารถใช้ซ้ำได้ evalเพียงแค่รันสตริงที่กำหนดและส่งกลับผลลัพธ์ของคำสั่งสุดท้าย คำถามของคุณเข้าใจผิดในขณะที่คุณพยายามสร้างฟังก์ชัน Wrapper ที่ใช้ฟังก์ชันเพื่อเลียนแบบ eval

เป็นความจริงที่พวกเขาแบ่งปันรหัสหลังม่าน? ใช่มีโอกาสมาก รหัสเดียวกันหรือไม่ ไม่อย่างแน่นอน

เพื่อความสนุกสนานนี่คือการใช้งานที่ไม่สมบูรณ์ของฉันเองโดยใช้ eval เพื่อสร้างฟังก์ชัน หวังว่ามันจะทำให้เกิดความแตกต่าง!

function makeFunction() {
  var params = [];
  for (var i = 0; i < arguments.length -  1; i++) {
    params.push(arguments[i]);
  }
  var code = arguments[arguments.length -  1];


 // Creates the anonymous function to be returned
 // The following line doesn't work in IE
 // return eval('(function (' + params.join(',')+ '){' + code + '})');
 // This does though
 return eval('[function (' + params.join(',')+ '){' + code + '}][0]');
}

ความแตกต่างที่ใหญ่ที่สุดระหว่างฟังก์ชันนี้กับฟังก์ชันใหม่คือฟังก์ชันไม่ได้กำหนดขอบเขตคำศัพท์ ดังนั้นมันจะไม่สามารถเข้าถึงตัวแปรการปิดและของฉันได้


ทำไมไม่ใช้arguments.slice()แทน for loop ล่ะ? [].slice.call(arguments, 1)หรือถ้าข้อโต้แย้งไม่ได้เป็นอาร์เรย์จริง
Xeoncross

3

เพียงแค่ต้องการชี้ให้เห็นไวยากรณ์ที่ใช้ในตัวอย่างที่นี่และความหมาย:

 var func = function(string) {
     return (new Function( 'return (' + string + ')' )());
 }

สังเกตว่าฟังก์ชัน (... ) () มีเครื่องหมาย "()" ต่อท้าย ไวยากรณ์นี้จะทำให้ func ดำเนินการฟังก์ชันใหม่และส่งคืนสตริงไม่ใช่ฟังก์ชันที่ส่งคืนสตริง แต่ถ้าคุณใช้สิ่งต่อไปนี้:

 var func = function(string) {
     return (new Function( 'return (' + string + ')' ));
 }

ตอนนี้ func จะส่งคืนฟังก์ชันที่ส่งคืนสตริง


2

ถ้าคุณหมายถึงมันจะให้ผลลัพธ์เหมือนกันใช่ไหม ... แต่การจะประเมิน (aka "ประเมินสตริงของ JavaScript นี้") จะง่ายกว่ามาก

แก้ไขด้านล่าง:

มันเหมือนกับว่า ... ปัญหาทางคณิตศาสตร์สองข้อนี้เหมือนกัน:

1 + 1

1 + 1 + 1 - 1 + 1 - 1 * 1/1


ทั้งคู่แยกวิเคราะห์สตริงใหม่หรือไม่
qwertymk

ทั้งคู่จะแยกวิเคราะห์สตริง (ในตัวอย่างโค้ดของคุณ) ไม่รู้ว่าคุณหมายถึงอะไร "re-parse"
Timothy Khouri

1
ฉันแน่ใจว่าทั้งคู่แยกวิเคราะห์ (ประเมิน) สตริงในบางจุด แต่Functionอ็อบเจ็กต์อาจเก็บสตริงไว้ในตัวแปรสมาชิกส่วนตัวevalเมื่อคุณเรียกใช้ฟังก์ชัน
palswim

1

ในตัวอย่างนั้นผลลัพธ์จะเหมือนกันใช่ ทั้งสองดำเนินการแสดงออกที่คุณส่งผ่าน นี่คือสิ่งที่ทำให้พวกเขาอันตรายมาก

แต่พวกเขาทำสิ่งที่แตกต่างกันหลังกลิ่น ฟังก์ชันที่เกี่ยวข้องกับnew Function()เบื้องหลังจะสร้างฟังก์ชันที่ไม่ระบุตัวตนจากรหัสที่คุณจัดหาซึ่งจะดำเนินการเมื่อเรียกใช้ฟังก์ชัน

JavaScript ที่คุณส่งผ่านไปจะไม่ถูกเรียกใช้งานในทางเทคนิคจนกว่าคุณจะเรียกใช้ฟังก์ชันนิรนาม ซึ่งตรงกันข้ามกับeval()ที่เรียกใช้โค้ดทันทีและไม่ได้สร้างฟังก์ชันตามโค้ด


คุณช่วยอธิบายเพิ่มเติมอีกหน่อยได้ไหม ทั้งคู่ไม่ได้ถูกดำเนินการในคำสั่งคืนสินค้าหรือไม่? ฟังก์ชัน () หนึ่งใช้ระดับการเรียกสแต็กอื่นที่มากกว่า eval หรือไม่?
qwertymk

Does the Function() one use another stack call level than eval?: ฉันจะถือว่าอย่างนั้นเพราะeval()ไม่ได้สร้างฟังก์ชั่นจากอินพุตของคุณมันแค่เรียกใช้งาน new Function()สร้างฟังก์ชันใหม่ซึ่งจะเรียกใช้
Chris Laplante

@SimpleCoder: Function () ไม่ได้เพิ่มอะไรลงใน call stack เฉพาะเมื่อคุณเรียกใช้ฟังก์ชันผลลัพธ์ eval ทำสิ่งเดียวกันทุกประการ (หากใช้ในบริบทของคำถาม)
Juan Mendes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.