Eval () và Hàm mới () có giống nhau không?


89

Hai chức năng này có làm cùng một việc ở hậu trường không? (trong các hàm câu lệnh đơn)

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
Trên thực tế, điều này được sử dụng trong jQuery 1.7.2 dòng 573. Mặc dù với "một số kiểm tra bảo mật"
PicoCreator

2
Tại sao được newxem xét trong cuộc thảo luận này? Functionmặc nhiên khởi tạo a function object. Loại trừ newsẽ không thay đổi mã nào cả. Đây là một Thể hiện jsfiddle rằng: jsfiddle.net/PcfG8
Travis J

Câu trả lời:


114

Không, chúng không giống nhau.

  • eval() đánh giá một chuỗi dưới dạng biểu thức JavaScript trong phạm vi thực thi hiện tại và có thể truy cập các biến cục bộ.
  • new Function()phân tích cú pháp mã JavaScript được lưu trữ trong một chuỗi thành một đối tượng hàm, sau đó có thể được gọi. Nó không thể truy cập các biến cục bộ vì mã chạy trong một phạm vi riêng biệt.

Hãy xem xét mã này:

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

Nếu new Function('return (a = 22);')()được sử dụng, biến cục bộ asẽ giữ nguyên giá trị của nó. Tuy nhiên, một số lập trình viên JavaScript như Douglas Crockford tin rằng không nên sử dụng cả hai mã này trừ khi thực sự cần thiết và việc đánh giá / sử dụng hàm Functiontạo trên dữ liệu không đáng tin cậy là không an toàn và không khôn ngoan.


10
"không bao giờ nên được sử dụng" là một tuyên bố rộng như vậy. Làm thế nào về, không bao giờ được sử dụng khi bất kỳ đầu vào nào nằm ngoài tầm kiểm soát của bạn. Phải nói rằng, tôi không bao giờ cần sử dụng nó, ngoại trừ phân tích cú pháp JSON. Nhưng tôi đã trả lời các câu hỏi ở đây có vẻ như là sử dụng hợp lệ, nó liên quan đến một thứ gì đó như cho phép quản trị viên tạo các hàm tạo mẫu mà người dùng cuối có thể sử dụng. Trong trường hợp này đầu vào được tạo ra bởi một quản trị viên, vì vậy nó là chấp nhận được
Juan Mendes

3
@Juan: Quan điểm của Crockford là eval thường bị sử dụng sai (tùy thuộc vào quan điểm của bạn), vì vậy nó nên được loại trừ khỏi JavaScript "các phương pháp hay nhất". Tuy nhiên, tôi đồng ý rằng nó hữu ích cho các mục đích sử dụng như JSON hoặc phân tích cú pháp biểu thức số học khi đầu vào được xác thực đúng cách lần đầu tiên. Ngay cả Crockford cũng sử dụng nó trong thư viện JSON json2.js của mình.
PleaseStand

Vì vậy, điều này có nghĩa là nó new Function(code)giống eval('(function(){'+code+'})()')hay nó là một bối cảnh thực thi hoàn toàn mới?
George Mauer

5
@GeorgeMauer: Ý bạn là eval('(function(){'+code+'})')nó sẽ trả về một đối tượng hàm. Theo MDN , "Các hàm được tạo bằng Hàm tạo hàm không tạo ra các bao đóng đối với ngữ cảnh tạo của chúng; chúng luôn chạy trong ngữ cảnh cửa sổ (trừ khi thân hàm bắt đầu bằng câu lệnh" sử dụng nghiêm ngặt "; trong trường hợp đó ngữ cảnh là không xác định) . "
PleaseStand

6

Không.

Trong bản cập nhật của bạn, các lệnh gọi đến evaluatefunctạo ra cùng một kết quả. Nhưng, họ chắc chắn không "làm những điều tương tự ở hậu trường". Các funcchức năng tạo ra một chức năng mới, nhưng ngay sau đó thực hiện nó, trong khi evaluatechức năng đơn giản thực thi mã ngay tại chỗ.

Từ câu hỏi ban đầu:

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

Những điều này sẽ cho bạn những kết quả rất khác nhau:

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

Trong mọi trường hợp, cả hai đều là hành vi xấu, trừ trường hợp đặc biệt nhất.
palswim

8
Vâng, bạn đã ngụy tạo ví dụ của bạn theo cách thực hiện của anh ta. Nếu ông thực hiện đánh giá như vậy return eval('(' + string + ')');thì chúng sẽ mang lại kết quả tương tự.
Juan Mendes

@Juan Tôi không nghĩ đến nhưng đúng vậy. Đã chỉnh sửa câu hỏi
qwertymk

6

new Functiontạo ra một chức năng có thể được sử dụng lại. evalchỉ thực hiện chuỗi đã cho và trả về kết quả của câu lệnh cuối cùng. Câu hỏi của bạn bị hiểu sai khi bạn cố gắng tạo một hàm trình bao bọc sử dụng Hàm để mô phỏng một đánh giá.

Có đúng là họ chia sẻ một số mã đằng sau rèm cửa? Có, rất có thể. Chính xác là cùng một mã? Không, chắc chắn.

Nói cho vui, đây là cách triển khai không hoàn hảo của riêng tôi bằng cách sử dụng eval để tạo một hàm. Hy vọng nó sẽ làm sáng tỏ sự khác biệt!

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]');
}

Sự khác biệt lớn nhất giữa Chức năng này và Chức năng mới là Chức năng không có phạm vi từ vựng. Vì vậy, nó sẽ không có quyền truy cập vào các biến đóng và của tôi thì có.


tại sao không sử dụng arguments.slice()thay vì vòng lặp for? Hoặc nếu các đối số không phải là một mảng đúng [].slice.call(arguments, 1),.
Xeoncross

3

Chỉ muốn chỉ ra một số cú pháp được sử dụng trong các ví dụ ở đây và ý nghĩa của nó:

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

chú ý rằng Hàm (...) () có dấu "()" ở cuối. Cú pháp này sẽ khiến func thực thi hàm mới và trả về chuỗi không phải là hàm trả về chuỗi, nhưng nếu bạn sử dụng như sau:

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

Bây giờ func sẽ trả về một hàm trả về một chuỗi.


2

Nếu ý bạn là, nó có mang lại kết quả tương tự không, thì có ... nhưng chỉ để đánh giá (hay còn gọi là "đánh giá chuỗi JavaScript này") sẽ đơn giản hơn nhiều.

CHỈNH SỬA Dưới đây:

Nó giống như nói rằng ... hai bài toán này có giống nhau không:

1 + 1

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


cả hai đều phân tích lại chuỗi?
qwertymk

cả hai sẽ phân tích cú pháp chuỗi (trong ví dụ mã của bạn). không biết ý bạn là "phân tích lại".
Timothy Khouri

1
Tôi chắc chắn rằng cả hai đều phân tích cú pháp (đánh giá) chuỗi tại một số điểm, nhưng Functionđối tượng có thể lưu chuỗi trong một biến thành viên riêng tư evalkhi bạn gọi hàm.
palswim

1

Trong ví dụ đó, kết quả là như nhau, vâng. Cả hai đều thực thi biểu thức mà bạn chuyển. Đây là những gì làm cho chúng rất nguy hiểm.

Nhưng họ làm những điều khác nhau đằng sau quyền trượng. Cái liên quan đến new Function(), đằng sau cảnh, tạo một hàm ẩn danh từ mã bạn cung cấp, được thực thi khi hàm được gọi.

Về mặt kỹ thuật, JavaScript bạn chuyển cho nó sẽ không được thực thi cho đến khi bạn gọi hàm ẩn danh. Điều này trái ngược với việc eval()thực thi mã ngay lập tức và không tạo ra một hàm dựa trên nó.


Bạn có thể giải thích thêm một chút không? Cả hai đều không được thực thi trong câu lệnh trả về? Hàm () có sử dụng mức gọi ngăn xếp khác so với eval không?
qwertymk

Does the Function() one use another stack call level than eval?: Tôi sẽ giả sử như vậy, bởi vì eval()không tạo một hàm từ đầu vào của bạn - nó chỉ thực thi nó. new Function()tạo một hàm mới, sau đó nó sẽ gọi.
Chris Laplante

@SimpleCoder: Hàm () không thêm bất kỳ thứ gì vào ngăn xếp cuộc gọi. Chỉ khi bạn gọi hàm kết quả. eval làm điều chính xác cùng (nếu áp dụng trong bối cảnh của câu hỏi)
Juan Mendes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.