摘要:花點時間搞清楚中的分號規(guī)則吧不管你喜歡結(jié)尾帶分號或省略分號的模式分號允許的場景分號一般允許出現(xiàn)在大部分語句的末尾,比如等栗子僅有一個分號可以表示空語句在中合法,比如可解析為三個空語句空語句可用于輔助產(chǎn)生語法合法的解析結(jié)果,如如果沒有末尾的
花點時間搞清楚JS中的分號規(guī)則吧~~~不管你喜歡結(jié)尾帶分號或省略分號的模式
分號允許的場景分號一般允許出現(xiàn)在大部分語句(statement)的末尾,比如 do-while statement , var statements, expression statements , continue , return , break statement, throw, debugger 等
栗子:
do Statement while ( Expression ) ; 4+4; f(); debugger;
僅有一個分號 ; 可以表示空語句——在JS中合法,比如 ;;; 可解析為三個空語句(empty statement)
空語句可用于輔助產(chǎn)生語法合法的解析結(jié)果,如:
while(1);
如果沒有末尾的分號,將會產(chǎn)生解析錯誤 —— 條件循環(huán)后必須跟隨一個語句
分號還會出現(xiàn)在 for 循環(huán) for ( Expression ; Expression ; Expression ) Statement 中
最后,分號還會出現(xiàn)在 字符串 或 正則表達式中 —— 表示分號本身
分號可以省略的場景有些場景下分號可以省略,解析器在解析語句時會根據(jù)需要自動插入分號,大概流程可以這樣理解:
書寫省略 => 解析器解析時發(fā)現(xiàn)缺少時會無法正確解析 => 自動添加分號
so 需要明確能自動插入分號的場景,并明確不會自動插入分號且會引起解析錯誤的情況
規(guī)則1:當(dāng)下一個 token (offending token) 和當(dāng)前解析的 token (previous token) 無法組成合法語句,且滿足以下一個或多個條件時,將會在 offending token 前插入一個分號:
offending token 和 previous token 被至少一個換行符分割(LineTerminator),且分號插入的作用不是被解析為 空語句 (empty statement)
offending token 是 }
previous token 是 ), 并且插入的分號將被解析為do-while語句的終止分號
還要考慮一種優(yōu)先級更高的條件:如果插入的分號會被解析為一個空語句,或是 for 語句的頭部兩個分號之一,這時不會插入分號(除了 do-while 語句的終止分號外)
規(guī)則2:當(dāng)解析到達源代碼文件 (input stream) 的末尾時,將自動添加一個分號標(biāo)識解析結(jié)束
規(guī)則3:符合 restricted production 語法的語句 —— 比較難翻譯,看不懂的可以直接看栗子,這種情況主要描述的是:不應(yīng)該出現(xiàn)換行符的地方出現(xiàn)換行符導(dǎo)致插入分號引起原語句含義變化
同時滿足以下條件,將在 offending token 前自動插入一個分號:
offending token 和 previous token 組成合語法的 restricted production 語句
offending token 出現(xiàn)于 restricted production 語句描述中的 [no LineTerminaator here] 部分 ( the token would be the first token for a terminal or nonterminal immediately following the annotation “[no LineTerminator here]” within the restricted production )
offending token 和 previous token 之間至少存在一個換行符 (LineTerminator)
其中 restricted production 包括且只有以下:
UpdateExpression[Yield, Await]: LeftHandSideExpression[?Yield, ?Await] [no LineTerminator here] ++ LeftHandSideExpression[?Yield, ?Await] [no LineTerminator here] -- ContinueStatement[Yield, Await]: continue; continue [no LineTerminator here] LabelIdentifier[?Yield, ?Await]; BreakStatement[Yield, Await]: break; break [no LineTerminator here] LabelIdentifier[?Yield, ?Await]; ReturnStatement[Yield, Await]: return; return [no LineTerminator here] Expression [+In, ?Yield, ?Await]; ThrowStatement[Yield, Await]: throw [no LineTerminator here] Expression [+In, ?Yield, ?Await]; ArrowFunction[In, Yield, Await]: ArrowParameters[?Yield, ?Await] [no LineTerminator here] => ConciseBody[?In] YieldExpression[In, Await]: yield [no LineTerminator here] * AssignmentExpression[?In, +Yield, ?Await] yield [no LineTerminator here] AssignmentExpression[?In, +Yield, ?Await]
簡單總結(jié):
使用 a++ 語句時,變量和 ++ 必須在同一行,否則會在 ++ 前插入分號導(dǎo)致語義不同
return throw yield continue break 后如果緊跟著換行,將會自動添加分號
箭頭函數(shù)的 => 之前不應(yīng)該有換行符
栗子 & 可能不符合預(yù)期的情況符合預(yù)期情況
// 相當(dāng)于 42;"hello" 42 "hello" // offending token 是 } if(x){y()} // previous token 是 ) 且插入分號是 do while 語句的結(jié)束 var a = 1 do {a++} while(a<100) console.log(a) // 不會解析成 b++ 因為 b和++之間存在換行符,會在 b 之后自動插入分號 a = b ++c
可能不符合預(yù)期的情況
const hey = "hey" const you = "hey" const heyYou = hey + " " + you ["h", "e", "y"].forEach((letter) => console.log(letter))
會收到錯誤 Uncaught TypeError: Cannot read property "forEach" of undefined , 因為 you 和 ["h", "e", "y"] 的連接能命中合法語法,故它們之間不會自動插入分號 —— 與預(yù)期不一致,JS嘗試將代碼解析為:
const hey = "hey"; const you = "hey"; const heyYou = hey + " " + you["h", "e", "y"].forEach((letter) => console.log(letter))
再看一種情況:
const a = 1 const b = 2 const c = a + b (a + b).toString()
會引發(fā) TypeError: b is not a function 報錯,因為會被解釋為:
const a = 1 const b = 2 const c = a + b(a + b).toString()
除了 do while 語句外,不會有插入分號作為空語句的其他情況,或作為 for 語句頭部的兩個必要分號 :
if (a > b) else c = d for (a; b )
以上均不是合法的 JS 語句,并且會引起報錯
故以下栗子中的每一個分號都不能省略??!
// for循環(huán)沒有循環(huán)體的情況,每一個分號都不能省略 for (node=getNode(); node.parent; node=node.parent) ;
再看一個帶詳細(xì)注釋的例子:
var // 這一行不會插入分號 ,因為 下一行的代碼不會破壞當(dāng)前行的代碼 a = 1 // 這一行會插入分號 let b = 2 // 再比如這種情況,你的原意可能是定義 `a` 變量,再執(zhí)行 `(a + 3).toString()`, // 但是其實 JavaScript 解析器解析成了,`var a = 2(a + 3).toString()`, // 這時會拋出錯誤 Uncaught TypeError: 2 is not a function var a = 2 (a + 3).toString() // 同理,下面的代碼會被解釋為 `a = b(function(){...})()` a = b (function(){ ... })()
以上都是未能命中規(guī)則1而未插入分號導(dǎo)致解析與預(yù)期不符合的情況
看一個基于規(guī)則3的例子:
(() => { return { color: "white" } })()
預(yù)期是返回一個包含 color 屬性的對象,但事實上 return 后會被插入一個分號,而導(dǎo)致最終返回 undefined,可以通過在 return 后立刻放置花括號 { :
(() => { return { color: "white" } })()省略分號的最佳實踐
不要使用以下單個字符 ( [ / + - 開始一行 , 會極有可能和上一行語句合在一起被解析( ++ 和 -- 不符合單個 +、- 字符)
注意 return break throw continue 語句,如果需要跟隨參數(shù)或表達式,把它添加到和這些語句同一行,針對 return 返回內(nèi)容較多的情況 (大對象,柯里化調(diào)用,多行字符串等),可以參考規(guī)則1,避免命中該規(guī)則而引起非預(yù)期的分號插入,比如:
return obj.method("abc") .method("xyz") .method("pqr") return "a long string " + "continued across " + "several lines" totalArea = rect_a.height * rect_a.width + rect_b.height * rect_b.width + circ.radius * circ.radius * Math.PI
后綴運算符 ++ -- 需要和操作變量在同一行使用
當(dāng)然大部分工程化情況下,我們最終會配合Eslint使用帶分號或省略分號規(guī)范~~~
參考:
ECMAScript規(guī)范
http://ecma-international.org/ecma-262/9.0/index.html#sec-automatic-semicolon-insertion
JavaScript 中的分號(;) – JavaScript 完全手冊(2018版)(翻譯版)
https://www.html.cn/archives/10061
JavaScript Semicolon Insertion
Everything you need to know
http://inimino.org/~inimino/blog/javascript_semicolons
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/102937.html
摘要:一自動插入分號規(guī)則三條規(guī)則要有換行符,且下一個符號是不符合語法的,那么就嘗試插入分號。有換行符,且語法中規(guī)定此處不能有換行符,那么就自動插入分號。源代碼結(jié)束處,不能形成完整的腳本或者模塊結(jié)構(gòu),那么就自動插入分號。 筆記說明 重學(xué)前端是程劭非(winter)【前手機淘寶前端負(fù)責(zé)人】在極客時間開的一個專欄,每天10分鐘,重構(gòu)你的前端知識體系,筆者主要整理學(xué)習(xí)過程的一些要點筆記以及感悟,完整...
摘要:一自動插入分號規(guī)則三條規(guī)則要有換行符,且下一個符號是不符合語法的,那么就嘗試插入分號。有換行符,且語法中規(guī)定此處不能有換行符,那么就自動插入分號。源代碼結(jié)束處,不能形成完整的腳本或者模塊結(jié)構(gòu),那么就自動插入分號。 筆記說明 重學(xué)前端是程劭非(winter)【前手機淘寶前端負(fù)責(zé)人】在極客時間開的一個專欄,每天10分鐘,重構(gòu)你的前端知識體系,筆者主要整理學(xué)習(xí)過程的一些要點筆記以及感悟,完整...
摘要:一自動插入分號規(guī)則三條規(guī)則要有換行符,且下一個符號是不符合語法的,那么就嘗試插入分號。有換行符,且語法中規(guī)定此處不能有換行符,那么就自動插入分號。源代碼結(jié)束處,不能形成完整的腳本或者模塊結(jié)構(gòu),那么就自動插入分號。 筆記說明 重學(xué)前端是程劭非(winter)【前手機淘寶前端負(fù)責(zé)人】在極客時間開的一個專欄,每天10分鐘,重構(gòu)你的前端知識體系,筆者主要整理學(xué)習(xí)過程的一些要點筆記以及感悟,完整...
摘要:與此相對,強類型語言的類型之間不一定有隱式轉(zhuǎn)換。三為什么是弱類型弱類型相對于強類型來說類型檢查更不嚴(yán)格,比如說允許變量類型的隱式轉(zhuǎn)換,允許強制類型轉(zhuǎn)換等等。在中,加性運算符有大量的特殊行為。 從++[[]][+[]]+[+[]]==10?深入淺出弱類型JS的隱式轉(zhuǎn)換 本文純屬原創(chuàng)? 如有雷同? 純屬抄襲? 不甚榮幸! 歡迎轉(zhuǎn)載! 原文收錄在【我的GitHub博客】,覺得本文寫的不算爛的...
閱讀 3057·2021-11-25 09:43
閱讀 1652·2021-11-24 11:15
閱讀 2373·2021-11-22 15:25
閱讀 3522·2021-11-11 16:55
閱讀 3257·2021-11-04 16:10
閱讀 2790·2021-09-14 18:02
閱讀 1698·2021-09-10 10:50
閱讀 1085·2019-08-29 15:39