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