(1, eval) ('this') so với eval ('this') trong JavaScript?


85

Tôi bắt đầu đọc các Mẫu JavaScript , một số làm tôi bối rối.

var global = (function () {
    return this || (1, eval)('this');
}());

Đây là những câu hỏi của tôi:

Q1:

(1, eval) === eval?

Tại sao và nó hoạt động như thế nào?

Q2: Tại sao không chỉ

var global = (function () {
    return this || eval('this');
}());

hoặc là

 var global = (function () {
    return this;
}());

Tôi đã cập nhật tiêu đề vì đây là một trường hợp cụ thể. Ngoài ra, dấu ngoặc đơn cho loại dấu ngoặc cụ thể : [] và {} hoàn toàn khác nhau :)

Câu trả lời:


104

Sự khác biệt giữa (1,eval)cũ và đơn giản evallà cái trước là một giá trị và cái sau là một giá trị. Sẽ rõ ràng hơn nếu đó là một số định danh khác:

var x;
x = 1;
(1, x) = 1; //  syntax error, of course!

Đó là (1,eval)một biểu thức mang lại eval(giống như nói, (true && eval)hoặc (0 ? 0 : eval)sẽ), nhưng nó không phải là một tham chiếu đến eval.

Bạn quan tâm làm gì?

Vâng, ECMA đặc tả xem xét một tài liệu tham khảo để evaltrở thành một "cuộc gọi eval trực tiếp", nhưng một biểu thức mà chỉ đơn thuần mang lại evallà một một gián tiếp - và các cuộc gọi eval gián tiếp được đảm bảo để thực hiện trong phạm vi toàn cầu.

Những điều tôi vẫn chưa biết:

  1. Trong trường hợp nào thì một lệnh gọi eval trực tiếp không thực thi trong phạm vi toàn cục?
  2. Trong trường hợp nào thì hàm thiscủa một hàm ở phạm vi toàn cục có thể không mang lại đối tượng toàn cục?

Có thể thu thập thêm một số thông tin tại đây .

BIÊN TẬP

Rõ ràng, câu trả lời cho câu hỏi đầu tiên của tôi là, "hầu như luôn luôn". Một evalthực thi trực tiếp từ phạm vi hiện tại . Hãy xem xét đoạn mã sau:

var x = 'outer';
(function() {
  var x = 'inner';
  eval('console.log("direct call: " + x)'); 
  (1,eval)('console.log("indirect call: " + x)'); 
})();

Không ngạc nhiên (heh-heh), điều này in ra:

direct call: inner
indirect call: outer

BIÊN TẬP

Sau nhiều thử nghiệm hơn, tôi sẽ tạm thời nói rằng thiskhông thể đặt thành nullhoặc undefined. Nó có thể được đặt thành các giá trị giả khác (0, '', NaN, false), nhưng chỉ rất cố ý.

Tôi sẽ nói rằng nguồn của bạn đang bị đảo ngược cranio-trực tràng nhẹ và có thể đảo ngược và có thể muốn xem xét dành một tuần lập trình trong Haskell.


3
Wow, không biết toàn bộ valueso với lvaluemọi thứ (tốt, có thể trong thực tế, nhưng không phải bằng lời). Cũng không phải các quy tắc đánh giá ES5 (không phải là điều mà tôi cần sử dụng một cách hợp lý eval). Cảm ơn!
Stoive

Vâng, evalcó rất nhiều cạnh sắc nhọn khó chịu và chỉ nên được sử dụng như một biện pháp cuối cùng và sau đó, rất, rất cẩn thận.
Malvolio

Tôi chỉ một lần tình cờ sử dụng hợp lệ - đánh giá thẻ tập lệnh đã được thêm vào DOM thông quainnerHtml
Stoive

1
lvalue ít liên quan đến việc xác định eval trực tiếp vì nó thường đề cập đến biểu thức có thể xuất hiện ở phía bên trái của một phép gán, do đó tên lvalue trái ngược với rvalue. Một lệnh gọi đánh giá chỉ trực tiếp trong các điều kiện được liệt kê trong 15.1.2.1.1 của thông số kỹ thuật cho biết mã định danh phải là evalvà là một phần của Biểu thức Thành viên của Biểu thức Gọi và tham chiếu đến evalhàm tiêu chuẩn .
chuckj

1
@Malvolio Có vẻ như bạn đang ngụ ý rằng các giá trị có liên quan đến đánh giá trực tiếp so với gián tiếp mà chúng không liên quan. Việc sử dụng một định danh được gọi evallà đích của một biểu thức cuộc gọi là đặc biệt. Bạn cho biết rằng ECMA coi tham chiếuevalđặc biệt mà nó không. Chính vị trí trong biểu thức cuộc gọi là đặc biệt mà biểu thức đó đánh giá evalhàm chuẩn . Ví dụ, var eval = window.eval; eval('1');vẫn là một đánh giá trực tiếp và window.eval('1')không phải mặc dù eval cũng là một giá trị trong trường hợp này.
chuckj

33

Mảnh vỡ,

var global = (function () {  
    return this || (1, eval)('this');  
}());  

sẽ đánh giá chính xác đối tượng toàn cục ngay cả trong chế độ nghiêm ngặt. Trong chế độ không nghiêm ngặt, giá trị của thislà đối tượng toàn cục nhưng ở chế độ nghiêm ngặt thì nó là đối tượng undefined. Biểu thức (1, eval)('this')sẽ luôn là đối tượng toàn cục. Lý do cho điều này liên quan đến các quy tắc xung quanh câu gián tiếp trực tiếp eval. Các cuộc gọi trực tiếp tới evalcó phạm vi của người gọi và chuỗi thissẽ đánh giá giá trị của thistrong bao đóng. Các evals gián tiếp đánh giá trong phạm vi toàn cục như thể chúng được thực thi bên trong một hàm trong phạm vi toàn cục. Vì bản thân hàm đó không phải là một hàm chế độ nghiêm ngặt nên đối tượng toàn cục được truyền vào thisvà sau đó biểu thức 'this'đánh giá đối tượng toàn cục. Biểu thức (1, eval)chỉ là một cách ưa thích để buộceval gián tiếp và trả về đối tượng toàn cục.

A1: (1, eval)('this')không giống như eval('this')vì các quy tắc đặc biệt xung quanh các cuộc gọi trực tiếp câu gián tiếp đến eval.

A2: Bản gốc hoạt động ở chế độ nghiêm ngặt, các phiên bản sửa đổi thì không.


12

Đến Q1:

Tôi nghĩ đây là một ví dụ điển hình về toán tử dấu phẩy trong JS. Tôi thích giải thích cho toán tử dấu phẩy trong bài viết này: http://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/

Toán tử dấu phẩy đánh giá cả hai toán hạng của nó (từ trái sang phải) và trả về giá trị của toán hạng thứ hai.

Đến Q2:

(1, eval)('this')được coi là lệnh gọi eval gián tiếp, lệnh gọi này trong ES5 thực thi mã trên toàn cầu. Vì vậy, kết quả sẽ là bối cảnh toàn cầu.

Xem http://perfectkills.com/global-eval-what-are-the-options/#evaling_in_global_scope


7

Q1: Nhiều câu lệnh javascript liên tiếp được phân tách bằng dấu phẩy nhận giá trị của câu lệnh cuối cùng. Vì thế:

(1, eval)nhận giá trị của giá trị cuối cùng là tham chiếu hàm đến eval()hàm. Rõ ràng nó làm theo cách này để biến eval()cuộc gọi thành một cuộc gọi eval gián tiếp sẽ được đánh giá trong phạm vi toàn cầu trong ES5. Chi tiết giải thích tại đây .

Câu hỏi 2: Phải có một số môi trường không xác định toàn cục this, nhưng lại xác định eval('this'). Đó là lý do duy nhất tôi có thể nghĩ ra cho điều đó.


Có lẽ ai đó đang cố gắng né tránh một cái móc đăng ký không cho phép /eval\(/g?
Stoive

@Stoive - vâng, tôi cũng thắc mắc về một thứ như vậy. Nếu không phải là móc đăng ký, một số bộ lọc ở đâu đó trong quá trình (có thể là giảm thiểu).
jfriend00 02/02/12

7
Nó liên quan đến chế độ nghiêm ngặt của ES5. AFAIK ở chế độ nghiêm ngặt ES5 bất kỳ evalmã 'd nào được thực thi trong ngữ cảnh riêng của nó chứ không phải trong ngữ cảnh chung hoặc ngữ cảnh bao quanh. Một cách để giải quyết vấn đề này là gián tiếp tham chiếu nó như mã được đề cập.
Cristian Sanchez

1
Hãy xem "Đánh giá toàn cầu. Các tùy chọn là gì? ".
Saxoier

Đã cập nhật câu trả lời của tôi để bao gồm thông tin từ CDSanchez và @Saxoier. Cám ơn.
jfriend00
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.