Các quy tắc để chèn dấu chấm phẩy tự động (ASI) của JavaScript là gì?


445

Chà, trước tiên tôi có lẽ nên hỏi nếu đây là phụ thuộc vào trình duyệt.

Tôi đã đọc rằng nếu tìm thấy mã thông báo không hợp lệ, nhưng phần mã hợp lệ cho đến khi mã thông báo không hợp lệ đó, dấu chấm phẩy được chèn trước mã thông báo nếu trước dòng bị ngắt.

Tuy nhiên, ví dụ phổ biến được trích dẫn cho các lỗi gây ra bởi chèn dấu chấm phẩy là:

return
  _a+b;

.. có vẻ như không tuân theo quy tắc này, vì _a sẽ là mã thông báo hợp lệ.

Mặt khác, phá vỡ chuỗi cuộc gọi hoạt động như mong đợi:

$('#myButton')
  .click(function(){alert("Hello!")});

Có ai có một mô tả sâu hơn về các quy tắc?


22
một đặc tả ...
Miles

33
@Miles Chỉ không ở liên kết bị hỏng của bạn ;-) ecma
Zach Lysobey

3
Xem p. 26 trong số PDF trích dẫn ở trên.
ᴠɪɴᴄᴇɴᴛ


tham khảo phần 11.9 Chèn dấu chấm phẩy tự động
Andrew Lam

Câu trả lời:


454

Trước hết, bạn nên biết những phát biểu nào bị ảnh hưởng khi chèn dấu chấm phẩy tự động (còn được gọi là ASI cho ngắn gọn):

  • tuyên bố trống rỗng
  • var tuyên bố
  • tuyên bố biểu thức
  • do-while tuyên bố
  • continue tuyên bố
  • break tuyên bố
  • return tuyên bố
  • throw tuyên bố

Các quy tắc cụ thể của ASI, được mô tả trong thông số kỹ thuật §11.9.1 Quy tắc chèn dấu chấm phẩy tự động

Ba trường hợp được mô tả:

  1. Khi gặp phải mã thông báo ( LineTerminatorhoặc }) mà ngữ pháp không cho phép, dấu chấm phẩy được chèn trước nó nếu:

    • Mã thông báo được tách biệt với mã thông báo trước đó ít nhất một LineTerminator.
    • Mã thông báo là }

    ví dụ :

    { 1
    2 } 3
    

    được chuyển thành

    { 1
    ;2 ;} 3;
    

    Các NumericLiteral 1đáp ứng các điều kiện đầu tiên, sau đây mã thông báo là một dòng terminator.
    Các 2đáp ứng các điều kiện thứ hai, sau đây là dấu hiệu }.

  2. Khi kết thúc luồng đầu vào của mã thông báo và trình phân tích cú pháp không thể phân tích luồng mã thông báo đầu vào dưới dạng một Chương trình hoàn chỉnh duy nhất, thì dấu chấm phẩy sẽ tự động được chèn vào cuối luồng đầu vào.

    ví dụ :

    a = b
    ++c
    

    được chuyển thành:

    a = b;
    ++c;
    
  3. Trường hợp này xảy ra khi một mã thông báo được cho phép bởi một số sản xuất ngữ pháp, nhưng sản xuất là sản xuất bị hạn chế , dấu chấm phẩy được tự động chèn vào trước mã thông báo bị hạn chế.

    Sản phẩm bị hạn chế:

    UpdateExpression :
        LeftHandSideExpression [no LineTerminator here] ++
        LeftHandSideExpression [no LineTerminator here] --
    
    ContinueStatement :
        continue ;
        continue [no LineTerminator here] LabelIdentifier ;
    
    BreakStatement :
        break ;
        break [no LineTerminator here] LabelIdentifier ;
    
    ReturnStatement :
        return ;
        return [no LineTerminator here] Expression ;
    
    ThrowStatement :
        throw [no LineTerminator here] Expression ; 
    
    ArrowFunction :
        ArrowParameters [no LineTerminator here] => ConciseBody
    
    YieldExpression :
        yield [no LineTerminator here] * AssignmentExpression
        yield [no LineTerminator here] AssignmentExpression
    

    Ví dụ cổ điển, với ReturnStatement:

    return 
      "something";
    

    được chuyển thành

    return;
      "something";
    

4
# 1: Mã thông báo không được ngữ pháp cho phép thường không phải là dấu kết thúc dòng, phải không (trừ khi bạn có nghĩa là các sản phẩm bị hạn chế từ # 3)? Nó nghĩ rằng bạn nên bỏ qua dấu ngoặc đơn. # 2 Không phải ví dụ chỉ hiển thị phần chèn sau ++ccho rõ ràng?
Bergi

2
xin lưu ý rằng ASI không thực sự cần phải "chèn dấu chấm phẩy", chỉ cần chấm dứt câu lệnh trong trình phân tích cú pháp của động cơ ...
16:00

1
những gì nó nói "dòng đầu vào", điều đó có nghĩa là "một dòng"? "Luồng mã thông báo đầu vào" đang làm cho nó trở nên khó hiểu hơn
phân cực

Liệu các liên kết spec làm việc cho bất cứ ai khác? Nó dẫn tôi đến một trang gần như trống rỗng có một liên kết chết trên đó.
intcreator

vui lòng giải thích làm thế nào, theo các quy tắc này, ví dụ bên dưới bởi 太極 者 無極 của "a [LineBreak] = [LineBreak] 3" vẫn hoạt động
Nir O.

45

Trực tiếp từ ECMA-262, Đặc điểm kỹ thuật ECMAScript phiên bản thứ năm :

7.9.1 Quy tắc chèn dấu chấm phẩy tự động

Có ba quy tắc cơ bản của dấu chấm phẩy:

  1. Khi, khi chương trình được phân tích cú pháp từ trái sang phải, một mã thông báo (được gọi là mã thông báo vi phạm ) gặp phải mà không được phép sản xuất ngữ pháp, thì dấu chấm phẩy sẽ tự động được chèn trước mã thông báo vi phạm nếu một hoặc nhiều mã sau đây điều kiện là đúng:
    • Mã thông báo vi phạm được tách biệt với mã thông báo trước đó ít nhất một mã thông báo LineTerminator.
    • Mã thông báo vi phạm là }.
  2. Khi chương trình được phân tích cú pháp từ trái sang phải, cuối dòng mã thông báo đầu vào gặp phải và trình phân tích cú pháp không thể phân tích luồng mã thông báo đầu vào dưới dạng một ECMAScript hoàn chỉnh Program, sau đó một dấu chấm phẩy được tự động chèn vào cuối luồng đầu vào.
  3. Khi, khi chương trình được phân tích cú pháp từ trái sang phải, một mã thông báo gặp phải được cho phép bởi một số sản xuất ngữ pháp, nhưng việc sản xuất là sản xuất bị hạn chế và mã thông báo sẽ là mã thông báo đầu tiên cho thiết bị đầu cuối hoặc nonterminal ngay sau chú thích " [không có LineTerminatorở đây] " trong sản xuất bị hạn chế (và do đó, mã thông báo đó được gọi là mã thông báo bị hạn chế) và mã thông báo bị hạn chế được phân tách khỏi mã thông báo trước đó bởi ít nhất một LineTerminator , sau đó dấu chấm phẩy được tự động chèn trước mã thông báo bị hạn chế.

Tuy nhiên, có một điều kiện ghi đè bổ sung cho các quy tắc trước: dấu chấm phẩy không bao giờ được chèn tự động nếu dấu chấm phẩy sau đó sẽ được phân tích thành một câu lệnh trống hoặc nếu dấu chấm phẩy đó sẽ trở thành một trong hai dấu chấm phẩy trong tiêu đề của forcâu lệnh (xem 12.6 .3).


44

Tôi không thể hiểu rõ 3 quy tắc đó trong thông số kỹ thuật - hy vọng sẽ có một thứ tiếng Anh đơn giản hơn - nhưng đây là những gì tôi thu thập được từ JavaScript: The Definitive Guide, 4th Edition, David Flanagan, O'Reilly, 2011:

Trích dẫn:

JavaScript không coi mỗi ngắt dòng là dấu chấm phẩy: nó thường coi ngắt dòng là dấu chấm phẩy chỉ khi nó không thể phân tích mã mà không có dấu chấm phẩy.

Một trích dẫn khác: cho mã

var a
a
=
3 console.log(a)

JavaScript không coi ngắt dòng thứ hai là dấu chấm phẩy vì nó có thể tiếp tục phân tích cú pháp câu lệnh dài hơn a = 3;

và:

hai trường hợp ngoại lệ cho quy tắc chung rằng JavaScript diễn giải dòng bị phá vỡ dưới dạng dấu chấm phẩy khi nó không thể phân tích dòng thứ hai dưới dạng tiếp tục của câu lệnh trên dòng đầu tiên. Ngoại lệ đầu tiên liên quan đến các tuyên bố trả lại, phá vỡ và tiếp tục

... Nếu ngắt dòng xuất hiện sau bất kỳ từ nào trong số này ... JavaScript sẽ luôn diễn giải ngắt dòng đó là dấu chấm phẩy.

... Ngoại lệ thứ hai liên quan đến các toán tử ++ và ... ... Nếu bạn muốn sử dụng một trong hai toán tử này làm toán tử hậu tố, chúng phải xuất hiện trên cùng một dòng với biểu thức mà chúng áp dụng. Mặt khác, ngắt dòng sẽ được coi là dấu chấm phẩy và ++ hoặc - sẽ được phân tích cú pháp dưới dạng toán tử tiền tố được áp dụng cho mã theo sau. Hãy xem xét mã này, ví dụ:

x 
++ 
y

Nó được phân tích cú pháp như x; ++y;, không phải làx++; y

Vì vậy, tôi nghĩ để đơn giản hóa nó, có nghĩa là:

Nói chung, JavaScript sẽ đối xử với nó như là sự tiếp nối của mã miễn là nó làm cho tinh thần - ngoại trừ 2 trường hợp: (1) sau khi một số từ khóa thích return, break, continue, và (2) nếu nó thấy ++hoặc --trên một dòng mới, sau đó nó sẽ thêm các ;vào cuối dòng trước đó.

Phần về "coi nó là sự tiếp tục của mã miễn là nó có ý nghĩa" làm cho nó có cảm giác như sự phù hợp tham lam của biểu thức chính quy.

Như đã nói ở trên, điều đó có nghĩa là returnvới ngắt dòng, trình thông dịch JavaScript sẽ chèn một;

(được trích dẫn lại: Nếu ngắt dòng xuất hiện sau bất kỳ từ nào trong số những từ này [chẳng hạn như return] ... JavaScript sẽ luôn diễn giải ngắt dòng đó là dấu chấm phẩy)

và vì lý do này, ví dụ kinh điển của

return
{ 
  foo: 1
}

sẽ không hoạt động như mong đợi, bởi vì trình thông dịch JavaScript sẽ coi nó là:

return;   // returning nothing
{
  foo: 1
}

Không được ngắt dòng ngay sau return:

return { 
  foo: 1
}

cho nó hoạt động đúng. Và bạn có thể tự chèn ;nếu bạn tuân theo quy tắc sử dụng ;sau bất kỳ câu lệnh nào:

return { 
  foo: 1
};

17

Về chèn dấu chấm phẩy và câu lệnh var, hãy cẩn thận quên dấu phẩy khi sử dụng var nhưng kéo dài nhiều dòng. Ai đó đã tìm thấy điều này trong mã của tôi ngày hôm qua:

    var srcRecords = src.records
        srcIds = [];

Nó chạy nhưng hiệu quả là khai báo / gán srcIds là toàn cục vì khai báo cục bộ với var trên dòng trước đó không còn được áp dụng vì câu lệnh đó được coi là kết thúc do chèn bán dấu hai chấm tự động.


4
Điều này là lý do tại sao tôi sử dụng jsLint
Zach Lysobey

1
JsHint / Lint ngay trong trình chỉnh sửa mã của bạn với phản hồi ngay lập tức :)
dmi3y

5
@balupton Khi dấu phẩy kết thúc dòng bị lãng quên, dấu chấm phẩy sẽ tự động được chèn. Trái với quy tắc, nó giống như một "gotcha".
Dexygen

1
Tôi nghĩ balupton là chính xác, đó là một sự khác biệt nếu bạn viết: var srcRecords = src.records srcIds = [];trong một dòng và quên dấu phẩy hoặc bạn viết "return a && b" và không quên gì cả ... nhưng ngắt dòng trước dấu a sẽ chèn dấu chấm phẩy tự động sau khi trở về, được xác định bởi các quy tắc ASI ...
Sebastian

3
Tôi nghĩ rằng sự rõ ràng của việc gõ var( let, const) trên mỗi dòng vượt trội hơn một phần của một giây để nhập nó.
mực

5

Mô tả theo ngữ cảnh nhất về Chèn dấu chấm phẩy tự động của JavaScript mà tôi đã tìm thấy xuất phát từ một cuốn sách về Phiên dịch thủ công .

Quy tắc chèn dấu chấm phẩy tự động của JavaScript là quy tắc lẻ. Trong khi các ngôn ngữ khác giả định hầu hết các dòng mới là có ý nghĩa và chỉ một số ít nên bị bỏ qua trong các câu lệnh nhiều dòng, thì JS giả định ngược lại. Nó coi tất cả các dòng mới của bạn là khoảng trắng vô nghĩa trừ khi nó gặp lỗi phân tích cú pháp. Nếu có, nó quay lại và cố gắng biến dòng mới trước đó thành dấu chấm phẩy để có được một cái gì đó có giá trị về mặt ngữ pháp.

Ông tiếp tục mô tả nó như bạn sẽ mã mùi .

Ghi chú thiết kế này sẽ biến thành một bản thiết kế nếu tôi đi vào chi tiết đầy đủ về cách thức hoạt động của nó, ít hơn tất cả các cách khác nhau đó là một ý tưởng tồi. Đó là một mớ hỗn độn. JavaScript là ngôn ngữ duy nhất tôi biết trong đó nhiều hướng dẫn về phong cách yêu cầu dấu chấm phẩy rõ ràng sau mỗi câu lệnh mặc dù về mặt lý thuyết, ngôn ngữ cho phép bạn bỏ qua chúng.


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.