Apa aturan untuk penyisipan titik koma otomatis (ASI) JavaScript?


445

Yah, pertama saya mungkin harus bertanya apakah ini tergantung pada browser.

Saya telah membaca bahwa jika token yang tidak valid ditemukan, tetapi bagian dari kode tersebut valid sampai token yang tidak valid itu, tanda titik koma dimasukkan sebelum token jika didahului oleh pemisah baris.

Namun, contoh umum yang dikutip untuk bug yang disebabkan oleh penyisipan titik koma adalah:

return
  _a+b;

..yang tampaknya tidak mengikuti aturan ini, karena _a akan menjadi token yang valid.

Di sisi lain, memecah rantai panggilan berfungsi seperti yang diharapkan:

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

Adakah yang memiliki deskripsi aturan yang lebih mendalam?


22
Ada adalah sebuah spesifikasi ...
Miles

33
@Miles Hanya tidak pada tautan Anda yang rusak ;-) ecma-international.org/publications/standards/Ecma-262.htm
Zach Lysobey

3
Lihat hal. 26 di atas dikutip PDF.
ᴠɪɴᴄᴇɴᴛ


lihat bagian 11.9 Penyisipan Titik Koma Otomatis
Andrew Lam

Jawaban:


454

Pertama-tama Anda harus tahu pernyataan mana yang dipengaruhi oleh penyisipan titik koma otomatis (juga dikenal sebagai ASI untuk singkatnya):

  • pernyataan kosong
  • var pernyataan
  • pernyataan ekspresi
  • do-while pernyataan
  • continue pernyataan
  • break pernyataan
  • return pernyataan
  • throw pernyataan

Aturan konkret ASI, dijelaskan dalam spesifikasi §11.9.1 Aturan Penyisipan Titik Koma Otomatis

Tiga kasus dijelaskan:

  1. Ketika token ( LineTerminatoratau }) ditemui yang tidak diizinkan oleh tata bahasa, tanda titik koma dimasukkan sebelum itu jika:

    • Token dipisahkan dari token sebelumnya oleh setidaknya satu LineTerminator.
    • Tokennya adalah }

    misalnya :

    { 1
    2 } 3
    

    ditransformasikan menjadi

    { 1
    ;2 ;} 3;
    

    Yang NumericLiteral 1memenuhi kondisi pertama, token berikut adalah terminator garis.
    Yang 2memenuhi kondisi kedua, token berikut adalah }.

  2. Ketika akhir aliran input token ditemukan dan parser tidak dapat mengurai aliran token input sebagai Program tunggal yang lengkap, maka titik koma secara otomatis dimasukkan di akhir aliran input.

    misalnya :

    a = b
    ++c
    

    ditransformasikan menjadi:

    a = b;
    ++c;
    
  3. Kasus ini terjadi ketika token diizinkan oleh beberapa produksi tata bahasa, tetapi produksi adalah produksi terbatas , tanda titik koma secara otomatis dimasukkan sebelum token terbatas.

    Produksi terbatas:

    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
    

    Contoh klasik, dengan ReturnStatement:

    return 
      "something";
    

    ditransformasikan menjadi

    return;
      "something";
    

4
# 1: Token yang tidak diizinkan oleh tata bahasa biasanya bukan terminator garis, bukankah itu (kecuali maksud Anda adalah produksi terbatas dari # 3)? Itu pikir Anda harus menghilangkan tanda kurung. # 2 Bukankah seharusnya contoh hanya menampilkan penyisipan setelah ++cuntuk kejelasan?
Bergi

2
harap dicatat ASI tidak perlu benar-benar "memasukkan titik koma", hanya untuk mengakhiri pernyataan dalam parser mesin ...
Aprillion

1
apa yang dikatakan "input stream", apakah itu berarti "satu baris"? "Input token stream" membuatnya agak sulit untuk dipahami
nonopolaritas

Apakah tautan spec berfungsi untuk orang lain? Itu membawa saya ke halaman yang hampir kosong yang memiliki tautan mati.
intcreator

tolong jelaskan bagaimana, menurut aturan ini, contoh di bawah ini oleh 太極 者 無極 而 生 dari "a [LineBreak] = [LineBreak] 3" masih bekerja
Nir O.

45

Langsung dari ECMA-262, Spesifikasi ECMAScript Fifth Edition :

7.9.1 Aturan Penyisipan Titik Koma Otomatis

Ada tiga aturan dasar penyisipan titik koma:

  1. Ketika, ketika program diuraikan dari kiri ke kanan, sebuah token (disebut token yang menyinggung ) ditemukan yang tidak diizinkan oleh produksi tata bahasa, maka tanda titik koma secara otomatis dimasukkan sebelum token yang menyinggung jika satu atau lebih dari yang berikut ini kondisinya benar:
    • Token yang menyinggung dipisahkan dari token sebelumnya oleh setidaknya satu LineTerminator.
    • Token yang menyinggung adalah }.
  2. Ketika, ketika program diuraikan dari kiri ke kanan, akhir aliran input token ditemui dan pengurai tidak dapat mengurai aliran token input sebagai satu ECMAScript lengkap Program, maka tanda titik koma secara otomatis dimasukkan di akhir input stream.
  3. Ketika, ketika program diuraikan dari kiri ke kanan, token ditemukan yang diizinkan oleh beberapa produksi tata bahasa, tetapi produksi adalah produksi terbatas dan token akan menjadi token pertama untuk terminal atau nonterminal segera setelah anotasi " [tidak ada di LineTerminatorsini] " dalam produksi terbatas (dan karenanya token tersebut disebut token terbatas), dan token terbatas dipisahkan dari token sebelumnya oleh setidaknya satu LineTerminator , maka tanda titik koma secara otomatis dimasukkan sebelum token terbatas.

Namun, ada kondisi tambahan tambahan pada aturan sebelumnya: tanda titik koma tidak pernah dimasukkan secara otomatis jika tanda titik kemudian akan diuraikan sebagai pernyataan kosong atau jika tanda titik tersebut akan menjadi salah satu dari dua titik koma di header forpernyataan (lihat 12.6 .3).


44

Saya tidak dapat memahami 3 aturan dalam spesifikasi terlalu baik - berharap untuk memiliki sesuatu yang lebih sederhana dalam bahasa Inggris - tetapi di sini adalah apa yang saya kumpulkan dari JavaScript: Panduan Definitif, Edisi 6, David Flanagan, O'Reilly, 2011:

Mengutip:

JavaScript tidak memperlakukan setiap jeda baris sebagai titik koma: ia biasanya memperlakukan jeda baris sebagai titik koma hanya jika tidak dapat menguraikan kode tanpa titik koma.

Kutipan lain: untuk kode

var a
a
=
3 console.log(a)

JavaScript tidak memperlakukan jeda baris kedua sebagai titik koma karena dapat melanjutkan penguraian pernyataan yang lebih panjang a = 3;

dan:

dua pengecualian pada aturan umum bahwa JavaScript menginterpretasikan baris terputus sebagai titik koma ketika tidak dapat menguraikan baris kedua sebagai kelanjutan dari pernyataan di baris pertama. Pengecualian pertama melibatkan pernyataan kembali, istirahat, dan melanjutkan

... Jika jeda baris muncul setelah kata-kata ini ... JavaScript akan selalu mengartikan jeda baris itu sebagai titik koma.

... Pengecualian kedua melibatkan operator ++ dan −− ... Jika Anda ingin menggunakan salah satu dari operator ini sebagai operator postfix, mereka harus muncul pada baris yang sama dengan ekspresi yang mereka gunakan. Jika tidak, jeda baris akan diperlakukan sebagai titik koma, dan ++ atau - akan diuraikan sebagai operator awalan yang diterapkan pada kode yang mengikuti. Pertimbangkan kode ini, misalnya:

x 
++ 
y

Diuraikan sebagai x; ++y;, bukan sebagaix++; y

Jadi saya pikir untuk menyederhanakannya, itu berarti:

Secara umum, JavaScript akan memperlakukannya sebagai kelanjutan dari kode selama itu masuk akal - kecuali 2 kasus: (1) setelah beberapa kata kunci seperti return, break, continue, dan (2) jika melihat ++atau --pada baris baru, maka akan menambah yang ;pada akhir baris sebelumnya.

Bagian tentang "memperlakukannya sebagai kelanjutan dari kode selama itu masuk akal" membuatnya terasa seperti pencocokan serakah ekspresi reguler.

Dengan kata di atas, itu artinya untuk returndengan istirahat baris, penerjemah JavaScript akan menyisipkan;

(dikutip lagi: Jika satu baris muncul setelah salah satu dari kata-kata ini [seperti return] ... JavaScript akan selalu menafsirkan garis tersebut sebagai titik koma)

dan karena alasan ini, contoh klasik dari

return
{ 
  foo: 1
}

tidak akan berfungsi seperti yang diharapkan, karena penerjemah JavaScript akan memperlakukannya sebagai:

return;   // returning nothing
{
  foo: 1
}

Tidak boleh ada line-break segera setelah return:

return { 
  foo: 1
}

agar berfungsi dengan benar. Dan Anda dapat menyisipkan ;diri Anda jika Anda mengikuti aturan menggunakan ;setelah pernyataan apa pun:

return { 
  foo: 1
};

17

Mengenai penyisipan titik koma dan pernyataan var, berhati-hatilah dengan melupakan koma saat menggunakan var tetapi menjangkau beberapa baris. Seseorang menemukan ini di kode saya kemarin:

    var srcRecords = src.records
        srcIds = [];

Itu berjalan tetapi efeknya adalah bahwa deklarasi / penugasan srcIds bersifat global karena deklarasi lokal dengan var pada baris sebelumnya tidak lagi diterapkan karena pernyataan itu dianggap selesai karena penyisipan semi-kolon otomatis.


4
ini agak mengapa saya menggunakan jsLint
Zach Lysobey

1
JsHint / Lint tepat di editor kode Anda dengan respon langsung :)
dmi3y

5
@balupton Ketika koma yang akan mengakhiri garis dilupakan, titik koma dimasukkan secara otomatis. Berbeda dengan aturan itu lebih seperti "gotcha".
Dexygen

1
Saya pikir balupton benar, ini perbedaan jika Anda menulis: var srcRecords = src.records srcIds = [];dalam satu baris dan lupakan koma atau Anda menulis "kembalikan a && b" dan jangan lupakan apa pun ... tetapi garis putus sebelum huruf a akan menyisipkan tanda titik koma otomatis setelah kembali, yang ditentukan oleh aturan ASI ...
Sebastian

3
Saya pikir kejelasan mengetik var( let, const) pada setiap baris lebih besar daripada fraksi detik yang diperlukan untuk mengetiknya.
squidbe

5

Deskripsi paling kontekstual dari Penyisipan Titik Koma Otomatis JavaScript yang saya temukan berasal dari sebuah buku tentang Crafting Interpreters .

Aturan "penyisipan titik koma otomatis" JavaScript adalah yang aneh. Jika bahasa lain menganggap sebagian besar baris baru bermakna dan hanya sedikit yang harus diabaikan dalam pernyataan multi-baris, JS berasumsi sebaliknya. Ini memperlakukan semua baris baru Anda sebagai ruang kosong yang tidak berarti kecuali ia menemukan kesalahan parse. Jika ya, itu akan kembali dan mencoba mengubah baris baru sebelumnya menjadi titik koma untuk mendapatkan sesuatu yang secara tata bahasa sah.

Dia melanjutkan untuk menggambarkannya seperti Anda akan kode bau .

Catatan desain ini akan berubah menjadi cacian desain jika saya masuk ke detail lengkap tentang bagaimana itu bahkan bekerja, apalagi semua berbagai cara yang merupakan ide yang buruk. Ini berantakan. JavaScript adalah satu-satunya bahasa yang saya tahu di mana banyak panduan gaya meminta tanda titik koma eksplisit setelah setiap pernyataan meskipun bahasa secara teoritis memungkinkan Anda mengelak dari mereka.


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.