前言
無論哪個(gè)團(tuán)隊(duì)血崭,代碼風(fēng)格統(tǒng)一的爭(zhēng)論是一個(gè)無解的話題本股,每次爭(zhēng)論起來混狠,各有各理屑墨,或執(zhí)著或偏激或喪心病狂拍桌憤然離去 :)
如果團(tuán)隊(duì)強(qiáng)制性用了一種代碼風(fēng)格后躁锁,符合自己心中標(biāo)準(zhǔn)的同學(xué),欣然樂嘻嘻卵史;不符合的可能會(huì)心中『圈養(yǎng)』草泥馬暗然敲碼战转;最厲害的情況是,自己之前寫的代碼在團(tuán)隊(duì)流程工具統(tǒng)一編譯代碼的時(shí)候被暗中格式化了以躯,此時(shí)心中就不再是一圈草泥馬了槐秧,至少是一個(gè)動(dòng)物公園。
我們之所以為代碼風(fēng)格統(tǒng)一爭(zhēng)論,因?yàn)榇蠹叶贾懒己么a風(fēng)格的重要性:專業(yè)刁标、可維護(hù)颠通,提高團(tuán)隊(duì)協(xié)作效率。
扯不完的分號(hào)問題
某些代碼風(fēng)格支持者的支持理由看上去似乎并不是基于這個(gè)『重要性』或其更本質(zhì)的觀點(diǎn)提出命雀,而更像為自己貼上『個(gè)性代碼』蒜哀、『流行語言風(fēng)格』、『我不喜歡』等標(biāo)簽吏砂,如 JavaScript 分號(hào)大戰(zhàn)中撵儿,某乎上的一些『semicolon less』支持者觀點(diǎn):
先看看最近一兩年最 Hit 的 @尤雨溪 大人:
沒有應(yīng)該不應(yīng)該,只有你自己喜歡不喜歡狐血。JavaScript 語法長(zhǎng)得 C-like 不代表它本質(zhì)上和 C 是一類語言淀歇,所有直覺性的 “當(dāng)然應(yīng)該加分號(hào)” 都是保守的、未經(jīng)深入思考的草率結(jié)論匈织。后來新設(shè)計(jì)的語言里可選分號(hào)的多得去了浪默,光是 “可以加分號(hào)但是大家都不加” 的語言就有:Go, Scala, Ruby, Python, Swift, Groovy...
大 V 尤很介懷沒有深入去了解 ASI 機(jī)制就發(fā)表哪種好哪種不好的同學(xué),而似乎更傾向?qū)⒘餍姓Z言風(fēng)格作為參考標(biāo)準(zhǔn)缀匕。
個(gè)人覺得并不是很有說服力纳决,因?yàn)楹苋菀鬃屛蚁氲揭粋€(gè)打比方:2012年西班牙的傳控踢法所向披靡,驚艷世界足壇乡小,難道追求簡(jiǎn)單實(shí)用的德國(guó)隊(duì)就一定要跟著踢傳控球才能說明自己是一支很優(yōu)秀的球隊(duì)了嗎阔加?說不通。
再來看看從 2012 年糾結(jié)到 2014 年的 @賀老濕:
【2014年1月更新】當(dāng)初鑒于本答案過長(zhǎng)而可能導(dǎo)致部分“分號(hào)黨”無法catch到我的主要論點(diǎn)满钟,原本打算重新修訂本答案胜榔。但是因時(shí)間精力因素未予重寫,且從本答案的支持來看湃番,為分號(hào)正本清源的目的已經(jīng)達(dá)到夭织,所以不再修訂本答案。
這里僅總結(jié)下“分號(hào)黨”推崇的“總是寫分號(hào)”風(fēng)格的最主要缺陷:
- 人總是有可能忘記寫分號(hào)吠撮。ASI導(dǎo)致無法區(qū)分是無意中忘記還是有意不寫(代碼折行)尊惰。
- “總是寫分號(hào)”并不能完全解決ASI缺陷(如return后換行會(huì)自動(dòng)插入分號(hào))。
- “}”后是否要加分號(hào)需要回溯到對(duì)應(yīng)“{”之前進(jìn)行語義判斷(是否是函數(shù)表達(dá)式)泥兰,成本遠(yuǎn)高于前置分號(hào)判斷(只要對(duì)行首字符進(jìn)行token判斷:是否是 [ ( + - / 五個(gè)符號(hào)之一)择浊。
用了很長(zhǎng)很長(zhǎng)的篇幅去回答,老濕這精神值得我們?nèi)W(xué)習(xí)逾条。但是琢岩,老濕似乎是在放大加分號(hào)的各種缺點(diǎn),然后給自己理由作出平衡师脂,然后作出選擇担孔。
我似乎同樣也不能接受這樣的理由江锨,因?yàn)樵趯?shí)際編碼中糕篇,如果養(yǎng)成良好的編碼習(xí)慣啄育,他所列的 1,2拌消,3 缺陷根本不是問題挑豌。
其他一些觀點(diǎn),很多都是隨著自己的喜愛隨性提出墩崩,如:
我是不加分號(hào)黨氓英,不加分號(hào)讓代碼更清新,對(duì)視覺的負(fù)擔(dān)也少一點(diǎn)
那有沒有實(shí)在一點(diǎn)的觀點(diǎn)鹦筹?有的铝阐,@玉伯大神的
看項(xiàng)目,如果是不加的項(xiàng)目铐拐,則不加徘键,比如 zepto
如果是加的項(xiàng)目,則加上遍蟋,比如 jquery
4 空格和 2 空格也是一樣吹害,兩種風(fēng)格我都習(xí)慣
很佛系很務(wù)實(shí)的答案是不是。實(shí)際工作中虚青,就應(yīng)該這樣它呀,懂得『執(zhí)生』,懂得尊重挟憔,為玉伯點(diǎn) Like⊙毯牛可惜绊谭,這并不是一個(gè)可以說服你用還是不用分號(hào)的答案。
引發(fā)這些爭(zhēng)論汪拥,無非是風(fēng)格問題达传,風(fēng)格的東西,必定是個(gè)人偏愛的東西迫筑,圍著風(fēng)格去討論宪赶,永遠(yuǎn)都是口水戰(zhàn),沒有結(jié)果脯燃。
所以搂妻,難道這個(gè)『分號(hào)問題』真的只是風(fēng)格的問題?
引發(fā)分號(hào)的爭(zhēng)論問題辕棚,ASI 機(jī)制是最直接原因欲主,那么去了解為何會(huì)產(chǎn)生 ASI 是最容易著手的邓厕,而最捷徑的方法,就是去看看 JavaScript 的發(fā)明者 Brenden Eich 對(duì)這個(gè)問題的看法扁瓢。正好有一篇文章就是他在 2012 年專門對(duì)這個(gè)問題發(fā)表的详恼,The infernal semicolon(套路深)
文章開頭第一句就放了狠話
Most of the comments in this semicolons in JS exchange make me sad.
在這個(gè) JS分號(hào)爭(zhēng)論 中的大多數(shù)評(píng)論讓我感到悲哀
這個(gè)爭(zhēng)論的標(biāo)題是『bootstrap-dropdown.js clearMenus() needs ; at the end』,引發(fā)爭(zhēng)論的代碼如下:
clearMenus()
!isActive && $parent.toggleClass('open')
復(fù)制代碼有人指出當(dāng)時(shí)的 JSMin 有bug引几,壓縮上面的代碼會(huì)出錯(cuò)昧互。
這時(shí)候,JSON伟桅、JSLint敞掘、JSMin 和 ADSafe 的創(chuàng)造者、ECMA JavaScript 2.0 標(biāo)準(zhǔn)化委員會(huì)委員贿讹、被 JavaScript 之父Brendan Eich 稱為 JavaScript 的大宗師渐逃、名著《JavaScript: The Good Parts》(中文版《JavaScript語言精粹》)的作者(來頭有點(diǎn)兇)Douglas Crockford 直接懟之:
That is insanely stupid code. I am not going to dumb down JSMin for this case.
TC39 is considering the use of ! as an infix operator. This code will break in the future. Fix it now. Learn to use semicolons properly. ! is not intended to be a statement separator. ; is.這代碼真尼瑪?shù)寞偪裆礨,我是不會(huì)為了這傻X的案例而去降低 JSMin 的級(jí)數(shù)
TC39正在考慮將『!』號(hào)作為中綴運(yùn)算符使用民褂,這個(gè)代碼不久將來就運(yùn)行不了茄菊。趕緊修復(fù)吧,學(xué)學(xué)怎么正確地使用分號(hào)赊堪∶嬷常『!』號(hào)并不是語句的分隔號(hào),『;』才是哭廉。
Brendan Eich 對(duì) Douglas Crockford 的回答以及強(qiáng)硬的態(tài)度婉轉(zhuǎn)表示贊同的同時(shí)脊僚,也保留了自己看法,大致思路如下:
用了不短的篇幅談了設(shè)計(jì) ASI 的初衷:
ASI is (formally speaking) a syntactic error correction procedure. If you start to code as if it were a universal significant-newline rule, you will get into trouble
ASI是一個(gè)句法錯(cuò)誤糾正的程式遵绰,如果你一開始編碼就將之視為普遍適用的新語句行規(guī)則辽幌,你會(huì)很大鍋
『句法錯(cuò)誤糾正的程式』,我理解就是一個(gè)容錯(cuò)方案椿访,并不是一個(gè)語法規(guī)則乌企!換個(gè)說法就是,我(當(dāng)然是Brendan Eich)發(fā)明這個(gè)語言的時(shí)候成玫,在語法上明確說明加酵,一個(gè)句子的結(jié)束應(yīng)該以『;』號(hào)作為結(jié)束,但是你們的編碼風(fēng)格實(shí)在不可控哭当,有可能會(huì)出現(xiàn)漏寫分號(hào)猪腕,為了防止你們?cè)诰幋a過程中由于個(gè)人原因忘記加『;』號(hào)而造成錯(cuò)誤,所以我加上 ASI 這個(gè)機(jī)制钦勘,盡可能地讓代碼能正常地運(yùn)行下去陋葡!
這似乎是作者當(dāng)年的一番好意,如今卻成了一個(gè)爭(zhēng)論的話題彻采,這并不是作者希望看到的脖岛,對(duì)這個(gè)現(xiàn)象朵栖,作者對(duì) ASI 的設(shè)計(jì)表現(xiàn)出一絲絲悔意:
I wish I had made newlines more significant in JS back in those ten days in May, 1995
? 我多希望在 1995 年 5 月那 10 多天的日子里,可以讓 JS 的斷句意義顯得更為重要(當(dāng)時(shí) Brendan Eich只用了10天的時(shí)間就把 JavaScript 設(shè)計(jì)出來了)柴梆。
但是從語言設(shè)計(jì)者的角度考慮陨溅,ASI 的存在似乎也是合理的,作者認(rèn)為绍在,一個(gè)編程語言在編碼風(fēng)格自由度的把控并不可控门扇,ASI 的設(shè)計(jì)可以讓其有更大的自由度:
Since when does any programming language not have syntax arguments? All living, practical languages that I know of, even those with indentation-based block structure and similar restrictions, have degrees of freedom of expression that allow abusage as well as good usage
有哪個(gè)編程語言是沒有語法上的爭(zhēng)論的?所有我了解的語言都有不錯(cuò)的表達(dá)自由度偿渡,甚至一些基于縮進(jìn)塊狀結(jié)構(gòu)或與其有相似限制的語言有著同樣的表達(dá)自由度
Language designers can try to reduce degrees of freedom, but not eliminate them completely.
語言設(shè)計(jì)者可以嘗試降低自由度臼寄,卻不可能完完全全地將之限制死
最后,作者給了一個(gè)明確的觀點(diǎn):
My two cents: be careful not to use ASI as if it gave JS significant newlines. And please don’t abuse && and || where the mighty if statement serves better.
注意不要使用ASI溜宽,盡管他給 JS 作了斷句判斷處理吉拳,如果強(qiáng)大的 if 語句可以正常使用的情況下,請(qǐng)不要濫用 && 和 ||
而站在語言設(shè)計(jì)者的角度适揉,對(duì)語言自由度的把控留攒,作者也表達(dá)了自己的態(tài)度:
I’ll also say that if it were up to me, in view of JS’s subtle and long history, I’d fix JSMin. But I would still log a grumpy comment or two first!
大致意思是,如果作者是 Douglas Crockford嫉嘀,縱觀 JS 的微妙而長(zhǎng)遠(yuǎn)的發(fā)展史炼邀,作者會(huì)修復(fù) JSMin,但如果碰到 Douglas Crockford 所指的傻X代碼剪侮,作者仍然會(huì)第一個(gè)站出來先怒懟幾句拭宁!
我們用自己的話來整理一下作者(下文的我)的觀點(diǎn):
- ASI 是一個(gè)句法錯(cuò)誤糾正的程式,是我對(duì)語言設(shè)計(jì)自由度把控的態(tài)度
- 很不幸瓣俯,ASI 卻燃起了 JS 是否加分號(hào)編碼風(fēng)格爭(zhēng)論戰(zhàn)火杰标,粉絲們過激的行為使我感到很 Sad
- 為此我感到后悔,希望自己在 95 年 5 月份的 10 多天設(shè)計(jì)時(shí)間里可以讓 JS 的斷句意義顯得更為重要彩匕!
- 但是我是一個(gè)很有態(tài)度的語言設(shè)計(jì)者腔剂,ASI 從語言設(shè)計(jì)角度上給予了語言更大的自由度,從這個(gè)層面上說推掸,ASI 并沒有錯(cuò)桶蝎!
- 繼續(xù)但是驻仅,可以正常擼通代碼的情況下我還是不建議使用 ASI谅畅,畢竟 ASI 的設(shè)計(jì)是用來處理句法錯(cuò)誤糾正的
- 對(duì)于不尊重語言語法規(guī)范的傻 X 代碼我依然會(huì)怒懟幾句,但是對(duì)語言設(shè)計(jì)自由度把控的態(tài)度我還是會(huì)堅(jiān)持噪服,所以我尊敬的大神 Douglas Crockford毡泻,如果我是你,我還是會(huì)怒懟傻 X 代碼的粘优,但同時(shí)我會(huì)修復(fù) JSMin 以表作為一個(gè)語言設(shè)計(jì)者應(yīng)有的態(tài)度仇味。
自動(dòng)分號(hào)插入機(jī)制 ASI
JavaScript 有著自動(dòng)分號(hào)插入的機(jī)制(Automatic Semicolon Insertion)呻顽,簡(jiǎn)稱 ASI。這是一個(gè)輔助性的功能丹墨,然后有一些情況要注意:
如果你這樣寫代碼:
return
a + b
那么自動(dòng)分號(hào)插入后會(huì)這樣:
return;
a + b;
更可能導(dǎo)致隱含BUG的狀況是:
a = b + c
(d + e).print()
他不會(huì)自動(dòng)插入分號(hào)廊遍,因?yàn)榈诙幸焕ㄌ?hào)開始,會(huì)被誤認(rèn)為是函數(shù)贩挣。
a = b + c(d + e).print();
那么這樣看來喉前,用分號(hào)才是最安全的做法咯!
如果你不想用分號(hào)王财,又怕出問題卵迂,v2ex 上有位童鞋給出了一個(gè)速記方案:
如果你寫 JS 代碼不喜歡帶分號(hào),而又搞不清什么時(shí)候必須加分號(hào)绒净,可以這么做:
- 在以 "("见咒、"[" 、"/"挂疆、"+"改览、"-" 開頭的語句前面都加上一個(gè)分號(hào)。