前言
先說(shuō)明我并沒(méi)有要大家都來(lái)不加分號(hào),而是回答"為什么可以不加分號(hào)"淋袖,或是"為何分號(hào)是選項(xiàng)可有可無(wú)鸿市?",或是"分號(hào)是在何時(shí)可以不加即碗?何時(shí)又一定要加焰情?何時(shí)又算多加了?"等問(wèn)題剥懒。
"不用分號(hào)作語(yǔ)句結(jié)尾"并不是"完全不使用分號(hào)"内舟,而是該加的時(shí)候加,不該加的時(shí)候不加初橘,不用分號(hào)作為語(yǔ)句結(jié)尾验游。實(shí)際上,我如果在改寫(xiě)別人的代碼或是像原本函式庫(kù)(如jquery)的風(fēng)格是有用分號(hào)(;)時(shí)保檐,為了統(tǒng)一撰寫(xiě)風(fēng)格耕蝉,也是會(huì)加的,基本上自己最近寫(xiě)的ES6夜只、React垒在、Redux等就不用分號(hào)(;)結(jié)尾。
分號(hào)(;)的作用
在JS里的分號(hào)(;)是什么作用盐肃?總結(jié)一下爪膊,它既是語(yǔ)句(表達(dá)式)的分隔,也可以作為語(yǔ)句的結(jié)尾砸王。
但只認(rèn)為分號(hào)(;)就一定是語(yǔ)句的結(jié)尾是有疑問(wèn)的,下面這兩個(gè)例子就是典型范例峦阁,也是很常見(jiàn)的錯(cuò)誤:
//有問(wèn)題的例子function add() {
var a = 1, b = 2;
return
a + b;
}
或是像下面這個(gè)谦铃,也是很常見(jiàn)的錯(cuò)誤示范:
//有問(wèn)題的例子function test() {
return
{
test: true
};
}
這兩例的函數(shù)回傳值必為 undefined ,而不是應(yīng)該回傳的值 3 與物件榔昔。為何驹闰?因?yàn)槟阌锌吹絩eturn先換行(回車(chē)),再接著要回傳的值了嗎撒会?也就是說(shuō)這兩個(gè)代碼在執(zhí)行時(shí)嘹朗,相當(dāng)于下面這樣的代碼(我只寫(xiě)一個(gè)出來(lái),另一例相同):
function test() {
return;
{ test: true };
}
return 因?yàn)橄葥Q了一行诵肛,視為語(yǔ)句結(jié)尾屹培,所以根本沒(méi)有可回傳的值,也就是在上例子中,回車(chē)(\n)先把 return 結(jié)尾掉了褪秀,后面你加的分號(hào)(;)是結(jié)尾到下一行的蓄诽。這邏輯代表回車(chē)(\n)在這一句語(yǔ)句結(jié)尾這功用上,比分號(hào)(;)還優(yōu)先媒吗。
那為何回車(chē)(\n)或換行仑氛,可以作為語(yǔ)句的結(jié)尾?來(lái)源是由于ECMAScript的自動(dòng)插入分號(hào)(Automatic Semicolon Insertion, 以下簡(jiǎn)稱(chēng)ASI)的標(biāo)準(zhǔn)闸英。在語(yǔ)句或一段代碼敘述后锯岖,加了回車(chē)(\n)后,JS剖析器會(huì)在執(zhí)行期間自動(dòng)幫你插入分號(hào)甫何,這樣理解了嗎嚎莉?所以不是回車(chē)(\n)的事,是因?yàn)樽詣?dòng)幫你加分號(hào)作結(jié)尾了沛豌。
自動(dòng)插入分號(hào)(ASI)這個(gè)規(guī)則一直在網(wǎng)上有爭(zhēng)議趋箩,有一群人認(rèn)為它是個(gè)容易造成誤解的設(shè)計(jì),有一群人認(rèn)為它這設(shè)計(jì)是正確的加派,應(yīng)該多多利用叫确,就像不需要在一般情況下再多此一舉,在語(yǔ)句后面加上分號(hào)(;)芍锦。不管你是支援哪一種竹勉,我認(rèn)為都需要理解其中的內(nèi)容,作為一個(gè)稱(chēng)職的JS開(kāi)發(fā)者娄琉,這是很基礎(chǔ)的一些知識(shí)次乓。
自動(dòng)插入分號(hào)(ASI)的5種例外情況
這里討論的就是用回車(chē)(\n)或斷行來(lái)作為語(yǔ)句的結(jié)尾,自動(dòng)插入分號(hào)(Automatic Semicolon Insertion)標(biāo)準(zhǔn)的特例情況孽水。在以下的4種情況是用回車(chē)(\n)或空一行是不會(huì)作自動(dòng)插入分號(hào)來(lái)讓語(yǔ)句作結(jié)尾票腰,雖是說(shuō)"特例",但大概也是每天都看得到女气,只是常不知道為何會(huì)這樣而已杏慰,看了你就知道了。大致說(shuō)明如下:
1. 當(dāng)這一行的語(yǔ)句是沒(méi)關(guān)閉的情況
例如數(shù)組(陣列)炼鞠、對(duì)象(物件)文字字面缘滥、圓括號(hào)之類(lèi),或是最后是個(gè)點(diǎn)號(hào)(.)或逗號(hào)(,)時(shí)谒主,也就是如果把這行結(jié)尾朝扼,就會(huì)產(chǎn)生不合法語(yǔ)句的情況。
注: 遇到等號(hào)(=)算未完整的表述式霎肯,如果指定值在下一行會(huì)略過(guò)自動(dòng)結(jié)尾作用
這種例子很常見(jiàn)擎颖,通常是在分開(kāi)內(nèi)容太長(zhǎng)的語(yǔ)句榛斯,或是讓值寫(xiě)得清楚的地方。我舉下面的兩個(gè)合法語(yǔ)法范例肠仪,你會(huì)很容易理解回車(chē)(\n)會(huì)在何時(shí)自動(dòng)結(jié)尾肖抱,雖然這些例子是不好閱讀的例子:
//第一例var a =
[ 1,2,3,
]
//第二例function test(a,
b,
c){console.log(a,b,c
)
}
2. 這一行是++或--
這規(guī)則是很怪異的,從來(lái)沒(méi)看過(guò)有人這樣寫(xiě)過(guò)异旧。JS會(huì)認(rèn)為++與--在這一行是要準(zhǔn)備來(lái)運(yùn)算執(zhí)行下一行的值意述,所以不結(jié)尾。下面是個(gè)合法范例:
a=1--
a
console.log(a)
3. 這一行是for()吮蛹、while()荤崇、do、if()或else潮针,但沒(méi)有用花括號(hào)({})時(shí)
這簡(jiǎn)寫(xiě)語(yǔ)法也很常見(jiàn)术荤,每天都會(huì)用到,是個(gè)當(dāng)執(zhí)行代碼不多時(shí)的寫(xiě)法每篷。以下為范例:
if(null==undefined)
console.log(true)else
console.log(false)
4. 下一行的開(kāi)頭是([)瓣戚、(()、(+)焦读、(*)子库、(/)、(-)矗晃、(,)仑嗅、(.),或二進(jìn)位運(yùn)算子(例如~ & |)张症,可以與這行組成一個(gè)表達(dá)式時(shí)
以下為范例:
function test(){
return 1
+2
-3
}console.log(test())
下面這個(gè)例子剛好與第一點(diǎn)顛倒的寫(xiě)法仓技,也很常見(jiàn),有人喜歡用這種的寫(xiě)法俗他,有些人喜歡用第一點(diǎn)的:
var a =
[1
,2
,]
5: 空白語(yǔ)句不會(huì)自動(dòng)結(jié)尾
這與上面第3點(diǎn)有相關(guān)脖捻,有時(shí)候會(huì)看到有人犯了這個(gè)常見(jiàn)錯(cuò)誤。下面是個(gè)錯(cuò)誤例子拯辙,不論 if 這行與 else 這行間有沒(méi)有換行郭变,或是換了幾百行,都不會(huì)自動(dòng)結(jié)尾:
//錯(cuò)誤例子var i = 10
if (i === 5)
else console.log(false)
你可以加上分號(hào)在 if(...) 這行最后涯保,讓語(yǔ)法合法可被執(zhí)行。但真心沒(méi)見(jiàn)過(guò)有人這樣用周伦,控制流程邏輯不對(duì)才會(huì)這樣寫(xiě)夕春。下面為合法例子:
//合法例子var i = 10
if (i === 5);
else console.log(false)
以上為用回車(chē)(n)作為語(yǔ)句結(jié)尾的的5個(gè)例外情況,其他都會(huì)自動(dòng)幫你插入分號(hào)作結(jié)尾专挪。
所以依這個(gè)解說(shuō)及志,最一開(kāi)始的例子中的 return 后面多了一個(gè)換行就會(huì)產(chǎn)生錯(cuò)誤的回傳值片排。與 return 有同樣情況的還有幾個(gè),像 continue 速侈、 break 率寡、 throw等,這些如果先換行倚搬,然后在下一行再加分號(hào)也是會(huì)變成兩個(gè)不同的語(yǔ)句冶共,因?yàn)樵趽Q行時(shí)已經(jīng)自動(dòng)插入分號(hào)。
一個(gè)小常識(shí)是每界,分號(hào)(;)除了作為語(yǔ)句結(jié)尾或分隔外捅僵,它與JS中的其他符號(hào)有個(gè)差異,它自己本身可以形成一個(gè)合法的語(yǔ)句(表達(dá)式)眨层。你可以在程式碼里只輸入一個(gè)分號(hào)(;)庙楚,它是合法而不會(huì)報(bào)錯(cuò)的,其他的符號(hào)(例如: ,:+-/%* )都會(huì)報(bào)錯(cuò)趴樱。
一定要使用分號(hào)的情況
分號(hào)(;)的用處不是只有語(yǔ)句結(jié)尾馒闷,它在某些語(yǔ)法中,具有分隔表述式或語(yǔ)句的功用叁征。以下情況必用分號(hào)(;)纳账。
1. for語(yǔ)句圓括號(hào)( () )中的三個(gè)表述式彼此之間:
很常用到,不多加說(shuō)明了航揉,以下為例子:
for(var i=0 ; i<10 ; i++)
{
//...
}
2. 在同一行寫(xiě)兩個(gè)語(yǔ)句(表述式)在一起塞祈,中間需要分號(hào)(;)區(qū)隔。
所以像 switch 語(yǔ)句中的 case 帅涂,如果 break (或 continue )要寫(xiě)在同一行時(shí)议薪, break (或 continue )前必加分號(hào)(;),以下為例子:
//第一例var i = 0; i++
//第二例case 'foo': doSomething(); break
3. 以 [ 或 ( 開(kāi)頭的行媳友,前面需要加分號(hào)(;)
這是很特別一種撰寫(xiě)風(fēng)格斯议。實(shí)際上是一種保護(hù)防范的語(yǔ)法,有時(shí)候解譯器或壓縮工具會(huì)誤認(rèn)為某行語(yǔ)句醇锚,有開(kāi)頭 ( 的話(huà)是準(zhǔn)備要作函數(shù)呼叫哼御,或開(kāi)頭 [ 是準(zhǔn)備要作數(shù)組(陣列)或?qū)ο?物件)的存取屬性的事。
還有另一情況是IIFE焊唬,因?yàn)镮IFE剛好也是用 ( 開(kāi)頭恋昼。所以這兩種開(kāi)頭的語(yǔ)句前,必加分號(hào)(;)在語(yǔ)句的最前面赶促,以免造成誤判液肌。不過(guò)大概也只有這一個(gè)需要特別注意,IIFE你用到的情況有可能會(huì)多些鸥滨。
以下為例子:
//第一例
;(x || y).doSomething()
;[a, b, c].forEach(doSomething)
//第二例var x = 42
;(function () { })()
參考: http://stackoverflow.com/ques...
"不需要"與"一定不能"使用分號(hào)的情況
接著要理解嗦哆,什么時(shí)候必"不需要"與"一定不能"使用分號(hào)(;)谤祖,這也很容易理解的。如果你是都要用分號(hào)來(lái)作每行語(yǔ)句的結(jié)尾老速,你應(yīng)該了解一下粥喜。
1. for 語(yǔ)句的最后一個(gè)(第三個(gè))表達(dá)式后面
畫(huà)蛇添足不多說(shuō),一定會(huì)造成錯(cuò)誤橘券,其實(shí)這錯(cuò)誤很難犯额湘,只是網(wǎng)上有我把它列出來(lái)。
//錯(cuò)誤語(yǔ)法for (var i=0; i < 10; i++;){}
2. 花括號(hào)的結(jié)尾(})的后面约郁。但有例外缩挑,賦值時(shí)可以加分號(hào)(;)是對(duì)的語(yǔ)法。
這個(gè)規(guī)則理論上應(yīng)該是"不需要"加而已鬓梅,經(jīng)測(cè)試在Chrome上也不會(huì)報(bào)錯(cuò)供置,算是不建議的語(yǔ)法,這也是個(gè)畫(huà)蛇添足绽快。
//不建議語(yǔ)法function a(){};if(){};
//正確語(yǔ)法芥丧,賦值時(shí)使用var obj = {a:1};var fun = function(){};
//正確語(yǔ)法,do...whiledo {...} while (...);
3. 在if坊罢、for续担、while、或switch的圓括號(hào)結(jié)尾后面加上分號(hào)(;)
這個(gè)是合法語(yǔ)法活孩,但整個(gè)邏輯錯(cuò)掉物遇,而且這錯(cuò)誤很也不易犯『度澹可以對(duì)照到最上面一節(jié)的第5個(gè)例外情況看看询兴。
//錯(cuò)誤例子,不過(guò)它合法if (0 === 1); { alert("hi") }
//上例相當(dāng)于下面這樣if (0 === 1);alert("hi");
反對(duì)的原因起趾,以及說(shuō)明
以下針對(duì)幾個(gè)常見(jiàn)的反對(duì)不使用分號(hào)作為語(yǔ)句結(jié)尾的理由诗舰,以我的所知回答這些原因。不過(guò)训裆,大部份都是因?yàn)镕UD(懼眶根、惑、疑)边琉,而不是真正以理解或科學(xué)實(shí)證的角度属百。
因?yàn)闉g覽器相容問(wèn)題。舊版的瀏覽器不支持变姨。
自動(dòng)插入分號(hào)(ASI)的標(biāo)準(zhǔn)是何時(shí)加到ECMAScript的诸老?
自動(dòng)插入分號(hào)其實(shí)是本來(lái)就有的語(yǔ)言特性,網(wǎng)上可以找到的2000年的ECMAScript版本3钳恕,也就是ES3標(biāo)準(zhǔn)别伏,里面就有這個(gè) Automatic Semicolon Insertion 章節(jié)了。
根據(jù)網(wǎng)上的文章指出忧额,曾經(jīng)有一小段時(shí)間IE6這個(gè)在ASI實(shí)作上有問(wèn)題厘肮,但后來(lái)很快修正了,現(xiàn)在我們能用到的IE6是支持的睦番。下圖是在WindowsXP SP3中的IE6小小測(cè)試的类茂,你可以仔細(xì)看一下,并沒(méi)有用分號(hào)來(lái)作語(yǔ)句結(jié)尾托嚣。
所以巩检,你是聽(tīng)誰(shuí)說(shuō)舊版的瀏覽器不支援的?該不會(huì)那個(gè)人又說(shuō)他也是聽(tīng)說(shuō)的示启。謠言止于智者對(duì)吧兢哭。
因?yàn)閴嚎s工具不支持
這原因以前的確存在過(guò),有個(gè)壓縮工具叫JSMin夫嗓,是大師Douglas Crockford寫(xiě)的工具迟螺,印象中如果你沒(méi)用分號(hào)作語(yǔ)句結(jié)尾,它是連壓都不壓舍咖,當(dāng)然Crockford他個(gè)人本來(lái)就不太贊成不用分號(hào)作語(yǔ)句結(jié)尾的關(guān)系矩父。
現(xiàn)在老早就沒(méi)有這問(wèn)題了,其他常用的工具如Closure Compiler或webpack中的外掛工具都可以壓好壓滿(mǎn)排霉,要不然怎么會(huì)有大專(zhuān)案(bootstrap, npm, vue.js)也舍棄用分號(hào)作語(yǔ)句結(jié)尾窍株。
最近的在JSMin與Bootstrap中的一段代碼爭(zhēng)議在這里可以看到,JS發(fā)明人Brendan Eich的言論立場(chǎng)是會(huì)中立些: https://brendaneich.com/2012/...
語(yǔ)句都用分號(hào)(;)作結(jié)尾是一個(gè)好的撰寫(xiě)風(fēng)格
最有名的是從 JSLint 這個(gè)檢查工具開(kāi)始的攻柠,它會(huì)檢查你的每個(gè)語(yǔ)句的最后是不是用分號(hào)(;)來(lái)作結(jié)尾球订,如果不是會(huì)提出警告信息。
而提倡在每個(gè)語(yǔ)句后面一定要用分號(hào)來(lái)作結(jié)尾的大師級(jí)人物辙诞,最有名的算是JSON格式發(fā)明者Douglas Crockford辙售, 他的這個(gè)文章 上有說(shuō)明,從文章中可以看得出他是反對(duì)ASI特性的飞涂。JSLint工具也是他作的旦部,相信也有很多人看過(guò)他的大作"JavaScript: 優(yōu)良部份"。不過(guò)较店,他也提倡了很多風(fēng)格士八,例如tab鍵相當(dāng)于4個(gè)空格,但現(xiàn)在一般都用2個(gè)空格梁呈,或是一行一個(gè)var變量定義婚度,而且要按照英文字母排列。大師說(shuō)的我們小小程序員當(dāng)然必定是要重視官卡,但并不是照單全收蝗茁。
回到我們的問(wèn)題中醋虏,在瀏覽器上能順便執(zhí)行的代碼語(yǔ)法,為何要強(qiáng)制被檢查工具認(rèn)為是有問(wèn)題的語(yǔ)法哮翘?
如果你都到處都加上分號(hào)(;)颈嚼,卻還會(huì)發(fā)生有錯(cuò)誤的情況(例如最上面的典型例子),那這代表起因是來(lái)自于對(duì)于語(yǔ)言本身的設(shè)計(jì)或標(biāo)準(zhǔn)的無(wú)知饭寺,而不是怪罪于語(yǔ)言本身的臭蟲(chóng)或設(shè)計(jì)問(wèn)題阻课。
現(xiàn)在流行的其他檢查工具,例如ESLint艰匙、JSHint等等限煞,就算有這項(xiàng)檢查也有選項(xiàng)可以關(guān)閉。這純粹是開(kāi)發(fā)團(tuán)隊(duì)或個(gè)人的選擇才是正確的员凝。
結(jié)語(yǔ)
要不要用分號(hào)(;)作為語(yǔ)句的結(jié)尾署驻,就視個(gè)人習(xí)慣或組織團(tuán)隊(duì)規(guī)定的撰寫(xiě)風(fēng)格了。不管你的選擇為何绊序,都應(yīng)該理解為何分號(hào)(;)是選項(xiàng)而非必要的原因硕舆,不要因?yàn)榇笊窕蛏缛荷洗蠹艺f(shuō)要加該加,就埋頭拼命加骤公,而不去理解其中的原因抚官。
真實(shí)情況是并不是所有的情況分號(hào)作為語(yǔ)句都是選項(xiàng),是有規(guī)則標(biāo)準(zhǔn)的阶捆。js雖然常常有坑凌节,但本質(zhì)上是死的東西,標(biāo)準(zhǔn)就已經(jīng)定在那里了洒试,上面我所說(shuō)的規(guī)則學(xué)好足以應(yīng)付9成情況倍奢,也可以讓你的代碼寫(xiě)起來(lái)更加有信心。
最后幾句垒棋,開(kāi)源專(zhuān)案中真的完全不使用分號(hào)(;)的專(zhuān)案最近多起來(lái)的現(xiàn)象卒煞,但最有名的是大家每天都在用的 npm 、 bootstrap 叼架,還有最近比較火紅的 vue.js 畔裕、 redux這幾個(gè),有興趣可以找找乖订。
文章來(lái)源:SegmentFault