javascript
这些被同事喷的JS代码风格你写过多少?
作者:殷榮檜@騰訊
來自:https://github.com/jackiewillen/blog/issues/14
現(xiàn)在寫代碼比以前好多了,代碼的格式都有 eslint、prettier、babel(寫新版語法) 這些來保證,然而,技術(shù)手段再高端都不能解決代碼可讀性(代碼能否被未來的自己和同事看懂)的問題,因?yàn)檫@個(gè)問題只有人自己才能解決。我們寫代碼要寫到下圖中左邊這樣基本上就功德圓滿了。
一、變量相關(guān)
(1)變量數(shù)量的定義NO:濫用變量
function?example()?{
????var?a?=?1;
????var?b?=?2;
????var?c?=?a+b;
????var?d?=?c+1;
????var?e?=?d+a;
????return?e;
}
YES: 數(shù)據(jù)只使用一次或不使用就無需裝到變量中
let?kpi?=?4;??//?沒用的就刪除掉,不然過三個(gè)月自己都不敢刪,怕是不是那用到了function?example()?{
????var?a?=?1;
????var?b?=?2;
????return?2a+b+1;
}
(2)變量的命名
NO:自我感覺良好的縮寫
let?fName?=?'jackie';?//?看起來命名挺規(guī)范,縮寫,駝峰法都用上,ESlint各種檢測(cè)規(guī)范的工具都通過,But,fName是啥?這時(shí)候,你是不是想說What?are?you?弄啥呢?let?lName?=?'willen';?//?這個(gè)問題和上面的一樣
YES:無需對(duì)每個(gè)變量都寫注釋,從名字上就看懂
?let?firstName?=?'jackie';?//?怎么樣,是不是一目了然。少被噴了一次?let?lastName?=?'willen';
??```
(3)特定的變量
NO:無說明的參數(shù)
if?(value.length?<?8)?{?//?為什么要小于8,8表示啥?長(zhǎng)度,還是位移,還是高度?Oh,my?God!!????....
}
YES:添加變量
const?MAX_INPUT_LENGTH?=?8;if?(value.length?<?MAX_INPUT_LENGTH)?{?//?一目了然,不能超過最大輸入長(zhǎng)度
????....
}
(4)變量的命名
NO:命名過于啰嗦
let?nameString;let?theUsers;
YES: 做到簡(jiǎn)潔明了
let?name;let?users;
(5)使用說明性的變量(即有意義的變量名)
NO:長(zhǎng)代碼不知道啥意思
const?address?=?'One?Infinite?Loop,?Cupertino?95014';const?cityZipCodeRegex?=?/^[^,]+[,s]+(.+?)s*(d{5})?$/;
saveCityZipCode(
??address.match(cityZipCodeRegex)[1],?//?這個(gè)公式到底要干嘛,對(duì)不起,原作者已經(jīng)離職了。自己看代碼
??address.match(cityZipCodeRegex)[2],?//?這個(gè)公式到底要干嘛,對(duì)不起,原作者已經(jīng)離職了。自己看代碼
);
YES:用變量名來解釋長(zhǎng)代碼的含義
const?address?=?'One?Infinite?Loop,?Cupertino?95014';const?cityZipCodeRegex?=?/^[^,]+[,s]+(.+?)s*(d{5})?$/;
const?[,?city,?zipCode]?=?address.match(cityZipCodeRegex)?||?[];
saveCityZipCode(city,?zipCode);
(6)避免使用太多的全局變量
NO:在不同的文件不停的定義全局變量
name.jswindow.name?=?'a';
hello.js
window.name?=?'b';
time.js
window.name?=?'c';??//三個(gè)文件的先后加載順序不同,都會(huì)使得window.name的值不同,同時(shí),你對(duì)window.name的修改了都有可能不生效,因?yàn)槟悴恢滥阈薷倪^之后別人是不是又在別的說明文件中對(duì)其的值重置了。所以隨著文件的增多,會(huì)導(dǎo)致一團(tuán)亂麻。
YES:少用或使用替代方案
你可以選擇只用局部變量。通過(){}的方法。
如果你確實(shí)用很多的全局變量需要共享,你可以使用vuex,redux或者你自己參考flux模式寫一個(gè)也行。
(7)變量的賦值。
NO:對(duì)于求值獲取的變量,沒有兜底。
const?MIN_NAME_LENGTH?=?8;let?lastName?=?fullName[1];
if(lastName.length?>?MIN_NAME_LENGTH)?{?//?這樣你就給你的代碼成功的埋了一個(gè)坑,你有考慮過如果fullName?=?['jackie']這樣的情況嗎?這樣程序一跑起來就爆炸。要不你試試。
????....
}
YES:對(duì)于求值變量,做好兜底。
const?MIN_NAME_LENGTH?=?8;let?lastName?=?fullName[1]?||?'';?//?做好兜底,fullName[1]中取不到的時(shí)候,不至于賦值個(gè)undefined,至少還有個(gè)空字符,從根本上講,lastName的變量類型還是String,String原型鏈上的特性都能使用,不會(huì)報(bào)錯(cuò)。不會(huì)變成undefined。
if(lastName.length?>?MIN_NAME_LENGTH)?{
????....
}
其實(shí)在項(xiàng)目中有很多求值變量,對(duì)于每個(gè)求值變量都需要做好兜底。
let?propertyValue?=?Object.attr?||?0;?//?因?yàn)镺bject.attr有可能為空,所以需要兜底。
但是,賦值變量就不需要兜底了。
let?a?=?2;?//?因?yàn)橛械琢?#xff0c;所以不要兜著。
let?myName?=?'Tiny';?//?因?yàn)橛械琢?#xff0c;所以不要兜著。
二、函數(shù)相關(guān)
(1)函數(shù)命名
NO:從命名無法知道返回值類型
function?showFriendsList()?{....}?//?現(xiàn)在問,你知道這個(gè)返回的是一個(gè)數(shù)組,還是一個(gè)對(duì)象,還是true?or?false。你能答的上來否?你能答得上來我請(qǐng)你吃松鶴樓的滿漢全席還請(qǐng)你不要當(dāng)真。Yes:對(duì)于返回true or false的函數(shù),最好以should/is/can/has開頭
function?shouldShowFriendsList()?{...}function?isEmpty()?{...}
function?canCreateDocuments()?{...}
function?hasLicense()?{...}
(2)功能函數(shù)最好為純函數(shù)
NO: 不要讓功能函數(shù)的輸出變化無常。
function?plusAbc(a,?b,?c)?{??//?這個(gè)函數(shù)的輸出將變化無常,因?yàn)閍pi返回的值一旦改變,同樣輸入函數(shù)的a,b,c的值,但函數(shù)返回的結(jié)果卻不一定相同。????????var?c?=?fetch('../api');
????????return?a+b+c;
}
YES:功能函數(shù)使用純函數(shù),輸入一致,輸出結(jié)果永遠(yuǎn)唯一
function?plusAbc(a,?b,?c)?{??//?同樣輸入函數(shù)的a,b,c的值,但函數(shù)返回的結(jié)果永遠(yuǎn)相同。????????return?a+b+c;
}
(3)函數(shù)傳參
NO:傳參無說明
page.getSVG(api,?true,?false);?//?true和false啥意思,一目不了然YES: 傳參有說明
page.getSVG({????imageApi:?api,
????includePageBackground:?true,?//?一目了然,知道這些true和false是啥意思
????compress:?false,
})
(4)動(dòng)作函數(shù)要以動(dòng)詞開頭
NO: 無法辨別函數(shù)意圖
function?emlU(user)?{????....
}
YES:動(dòng)詞開頭,函數(shù)意圖就很明顯
function?sendEmailToUser(user)?{????....
}
(5)一個(gè)函數(shù)完成一個(gè)獨(dú)立的功能,不要一個(gè)函數(shù)混雜多個(gè)功能
這是軟件工程中最重要的一條規(guī)則,當(dāng)函數(shù)需要做更多的事情時(shí),它們將會(huì)更難進(jìn)行編寫、測(cè)試、理解和組合。當(dāng)你能將一個(gè)函數(shù)抽離出只完成一個(gè)動(dòng)作,他們將能夠很容易的進(jìn)行重構(gòu)并且你的代碼將會(huì)更容易閱讀。如果你嚴(yán)格遵守本條規(guī)則,你將會(huì)領(lǐng)先于許多開發(fā)者。
NO:函數(shù)功能混亂,一個(gè)函數(shù)包含多個(gè)功能。最后就像能以一當(dāng)百的老師傅一樣,被亂拳打死(亂拳(功能復(fù)雜函數(shù))打死老師傅(老程序員))
function?sendEmailToClients(clients)?{??clients.forEach(client?=>?{
????const?clientRecord?=?database.lookup(client)
????if?(clientRecord.isActive())?{
??????email(client)
????}
??})
}
YES: 功能拆解,
function?sendEmailToActiveClients(clients)?{??//各個(gè)擊破,易于維護(hù)和復(fù)用??clients.filter(isActiveClient).forEach(email)
}
function?isActiveClient(client)?{
??const?clientRecord?=?database.lookup(client)
??return?clientRecord.isActive()
}
(6)優(yōu)先使用命令式編程
NO: 使用for循環(huán)編程
for(i?=?1;?i?<=?10;?i++)?{?//?一看到for循環(huán)讓人頓生不想看的情愫???a[i]?=?a[i]?+1;
}
YES:使用命令式編程
let?b?=?a.map(item?=>?++item)?//?怎么樣,是不是很好理解,就是把a(bǔ)的值每項(xiàng)加一賦值給b就可以了。現(xiàn)在在javascript中幾乎所有的for循環(huán)都可以被map,filter,find,some,any,forEach等命令式編成取代。(7)函數(shù)中過多的采用if else ..
No: if else過多
if?(a?===?1)?{????...
}?else?if?(a?===2)?{
????...
}?else?if?(a?===?3)?{
????...
}?else?{
???...
}
YES: 可以使用switch替代或用數(shù)組替代
switch(a)?{???case?1:
???????????....
???case?2:
???????????....
???case?3:
???????????....
??default:
???????....
}
Or
let?handler?=?{
????1:?()?=>?{....},
????2:?()?=>?{....}.
????3:?()?=>?{....},
????default:?()?=>?{....}
}
handler[a]()?||?handler['default']()
三、盡量使用ES6,有可以能的話ES7中新語法
(只羅列最常用的新語法,說實(shí)話,有些新語法不怎么常用)
(1)盡量使用箭頭函數(shù)
NO:采用傳統(tǒng)函數(shù)
function?foo()?{??//?code
}
YES:使用箭頭函數(shù)
let?foo?=?()?=>?{
??//?code
}
(2)連接字符串
NO:采用傳統(tǒng)+號(hào)
var?message?=?'Hello?'?+?name?+?',?it's?'?+?time?+?'?now'YES:采用模板字符
var?message?=?`Hello?${name},?it's?${time}?now`(3)使用解構(gòu)賦值
NO:使用傳統(tǒng)賦值:
var?data?=?{?name:?'dys',?age:?1?};var?name?=?data.name;
var?age?=?data.age;
var?fullName?=?['jackie',?'willen'];
var?firstName?=?fullName[0];
var?lastName?=?fullName[1];
YES:使用結(jié)構(gòu)賦值:
const?data?=?{name:'dys',?age:1};const?{name,?age}?=?data;???//?怎么樣,是不是簡(jiǎn)單明了
var?fullName?=?['jackie',?'willen'];
const?[firstName,?lastName]?=?fullName;
(4) 盡量使用類class
NO: 采用傳統(tǒng)的函數(shù)原型鏈實(shí)現(xiàn)繼承
典型的?ES5?的類(function)在繼承、構(gòu)造和方法定義方面可讀性較差,當(dāng)需要繼承時(shí),優(yōu)先選用?class。代碼太多,就省略了。YES:采用ES6類實(shí)現(xiàn)繼承
class?Animal?{??constructor(age)?{
????this.age?=?age
??}
??move()?{
????/*?...?*/
??}
}
class?Mammal?extends?Animal?{
??constructor(age,?furColor)?{
????super(age)
????this.furColor?=?furColor
??}
??liveBirth()?{
????/*?...?*/
??}
}
class?Human?extends?Mammal?{
??constructor(age,?furColor,?languageSpoken)?{
????super(age,?furColor)
????this.languageSpoken?=?languageSpoken
??}
??speak()?{
????/*?...?*/
??}
}
先寫到這了,這是目前為止發(fā)現(xiàn)的問題,這篇文章中并沒有完全覆蓋到常見的寫代碼的不好的習(xí)慣,所以你如果覺的有需要補(bǔ)充的,都可以在文章下方評(píng)論,或者直接到我的Github的這篇文章中評(píng)論。對(duì)于有用的,都將補(bǔ)充到我的掘金和Github中去。同時(shí),你如果覺的文章寫得還可以,Please在我的Github中送上你寶貴的Star,你的Star是我繼續(xù)寫文章最大的動(dòng)力。
注:除了上述這些人為習(xí)慣之外,就像前面提到的,對(duì)于機(jī)械性的,你可以使用Babel、Eslint、Prettier這些工具來保證代碼的格式一致。
參考資料
https://blog.risingstack.com/javascript-clean-coding-best-practices-node-js-at-scale/(JavaScript Clean Coding Best Practices)
https://www.zhihu.com/question/20635785?(如何寫出優(yōu)美的 JavaScript 代碼?)
總結(jié)
以上是生活随笔為你收集整理的这些被同事喷的JS代码风格你写过多少?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决html2canvas截取页面部分d
- 下一篇: repalce