教你如何作一個優雅的Ecmascripter /轉

看看這些被同事噴的JS代碼風格你寫過多少

殷榮檜 JavaScript  今天javascript

如今寫代碼比之前好多了,代碼的格式都有eslint,prettier,babel(寫新版語法)這些來保證,然而,技術手段再高端都不能解決代碼可讀性(代碼可否被將來的本身和同事看懂)的問題,由於這個問題只有人本身才能解決。咱們寫代碼要寫到下圖中左邊這樣基本上就功德圓滿了。vue

注:因爲我的水平與眼界的緣由,這篇文章中並無徹底覆蓋到常見的寫代碼的很差的習慣,因此你若是覺的有須要補充的,均可以在文章下方評論,或者直接到個人Github的這篇文章中評論。對於有用的,都將補充到個人掘金和Github中去。同時,你若是覺的文章寫得還能夠,Please在個人Github中送上你寶貴的Star,你的Star是我繼續寫文章最大的動力。java

1、變量相關

(1)變量數量的定義node

NO:濫用變量:程序員

 
  1. let kpi = 4;  // 定義好了以後再也沒用過vuex

  2. function example() {編程

  3.    var a = 1;redux

  4.    var b = 2;api

  5.    var c = a+b;數組

  6.    var d = c+1;

  7.    var e = d+a;

  8.    return e;

  9. }

YES: 數據只使用一次或不使用就無需裝到變量中

 
  1. let kpi = 4;  // 沒用的就刪除掉,否則過三個月本身都不敢刪,怕是否是那用到了

  2. function example() {

  3.    var a = 1;

  4.    var b = 2;

  5.    return 2a+b+1;

  6. }

(2)變量的命名

NO:自我感受良好的縮寫

 
  1. let fName = 'jackie'; // 看起來命名挺規範,縮寫,駝峯法都用上,ESlint各類檢測規範的工具都經過,But,fName是啥?這時候,你是否是想說What are you 弄啥呢?

  2. let lName = 'willen'; // 這個問題和上面的同樣

YES:無需對每一個變量都寫註釋,從名字上就看懂

 
  1.  let firstName = 'jackie'; // 怎麼樣,是否是一目瞭然。少被噴了一次

  2.  let lastName = 'willen';

(3)特定的變量

NO:無說明的參數

 
  1. if (value.length < 8) { // 爲何要小於8,8表示啥?長度,仍是位移,仍是高度?Oh,my God!!

  2.    ....

  3. }

YES:添加變量

 
  1. const MAX_INPUT_LENGTH = 8;

  2. if (value.length < MAX_INPUT_LENGTH) { // 一目瞭然,不能超過最大輸入長度

  3.    ....

  4. }

(4)變量的命名

NO:命名過於囉嗦

 
  1. let nameString;

  2. let theUsers;

YES: 作到簡潔明瞭

 
  1. let name;

  2. let users;

(5)使用說明性的變量(即有意義的變量名)

NO:長代碼不知道啥意思

 
  1. const address = 'One Infinite Loop, Cupertino 95014';

  2. const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;

  3. saveCityZipCode(

  4.  address.match(cityZipCodeRegex)[1], // 這個公式到底要幹嗎,對不起,原做者已經離職了。本身看代碼

  5.  address.match(cityZipCodeRegex)[2], // 這個公式到底要幹嗎,對不起,原做者已經離職了。本身看代碼

  6. );

YES:用變量名來解釋長代碼的含義

 
  1. const address = 'One Infinite Loop, Cupertino 95014';

  2. const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;

  3. const [, city, zipCode] = address.match(cityZipCodeRegex) || [];

  4. saveCityZipCode(city, zipCode);

(6)避免使用太多的全局變量

NO:在不一樣的文件不停的定義全局變量

 
  1. name.js

  2. window.name = 'a';

  3. hello.js

  4. window.name = 'b';

  5. time.js

  6. window.name = 'c';  //三個文件的前後加載順序不一樣,都會使得window.name的值不一樣,同時,你對window.name的修改了都有可能不生效,由於你不知道你修改過以後別人是否是又在別的說明文件中對其的值重置了。因此隨着文件的增多,會致使一團亂麻。

YES:少用或使用替代方案 你能夠選擇只用局部變量。經過(){}的方法。

 
  1. 若是你確實用不少的全局變量須要共享,你可使用vuex,redux或者你本身參考flux模式寫一個也行。

(7) 變量的賦值。

NO:對於求值獲取的變量,沒有兜底。

 
  1. const MIN_NAME_LENGTH = 8;

  2. let lastName = fullName[1];

  3. if(lastName.length > MIN_NAME_LENGTH) { // 這樣你就給你的代碼成功的埋了一個坑,你有考慮過若是fullName = ['jackie']這樣的狀況嗎?這樣程序一跑起來就爆炸。要不你試試。

  4.    ....

  5. }

YES:對於求值變量,作好兜底。

 
  1. const MIN_NAME_LENGTH = 8;

  2. let lastName = fullName[1] || ''; // 作好兜底,fullName[1]中取不到的時候,不至於賦值個undefined,至少還有個空字符,從根本上講,lastName的變量類型仍是String,String原型鏈上的特性都能使用,不會報錯。不會變成undefined。

  3. if(lastName.length > MIN_NAME_LENGTH) {

  4.    ....

  5. }

  6. 其實在項目中有不少求值變量,對於每一個求值變量都須要作好兜底。

  7. let propertyValue = Object.attr || 0; // 由於Object.attr有可能爲空,因此須要兜底。

  8. 可是,賦值變量就不須要兜底了。

  9. let a = 2; // 由於有底了,因此不要兜着。

  10. let myName = 'Tiny'; // 由於有底了,因此不要兜着。

2、函數相關

(1)函數命名

NO:從命名沒法知道返回值類型

 
  1. function showFriendsList() {....} // 如今問,你知道這個返回的是一個數組,仍是一個對象,仍是true or false。你能答的上來否?你能答得上來我請你吃松鶴樓的滿漢全席還請你不要當真。

Yes: 對於返回true or false的函數,最好以should/is/can/has開頭

 
  1. function shouldShowFriendsList() {...}

  2. function isEmpty() {...}

  3. function canCreateDocuments() {...}

  4. function hasLicense() {...}

(2) 功能函數最好爲純函數

NO: 不要讓功能函數的輸出變化無常。

 
  1. function plusAbc(a, b, c) {  // 這個函數的輸出將變化無常,由於api返回的值一旦改變,一樣輸入函數的a,b,c的值,但函數返回的結果卻不必定相同。

  2.        var c = fetch('../api');

  3.        return a+b+c;

  4. }

YES:功能函數使用純函數,輸入一致,輸出結果永遠惟一

 
  1. function plusAbc(a, b, c) {  // 一樣輸入函數的a,b,c的值,但函數返回的結果永遠相同。

  2.        return a+b+c;

  3. }

(3)函數傳參

NO:傳參無說明

 
  1. page.getSVG(api, true, false); // true和false啥意思,一目不了然

YES: 傳參有說明

 
  1. page.getSVG({

  2.    imageApi: api,

  3.    includePageBackground: true, // 一目瞭然,知道這些true和false是啥意思

  4.    compress: false,

  5. })

(4)動做函數要以動詞開頭

NO: 沒法辨別函數意圖

 
  1. function emlU(user) {

  2.    ....

  3. }

YES:動詞開頭,函數意圖就很明顯

 
  1. function sendEmailToUser(user) {

  2.    ....

  3. }

(5)一個函數完成一個獨立的功能,不要一個函數混雜多個功能

這是軟件工程中最重要的一條規則,當函數須要作更多的事情時,它們將會更難進行編寫、測試、理解和組合。當你能將一個函數抽離出只完成一個動做,他們將可以很容易的進行重構而且你的代碼將會更容易閱讀。若是你嚴格遵照本條規則,你將會領先於許多開發者。

NO:函數功能混亂,一個函數包含多個功能。最後就像能以一當百的老師傅同樣,被亂拳打死(亂拳(功能複雜函數)打死老師傅(老程序員))

 
  1. function sendEmailToClients(clients) {

  2.  clients.forEach(client => {

  3.    const clientRecord = database.lookup(client)

  4.    if (clientRecord.isActive()) {

  5.      email(client)

  6.    }

  7.  })

  8. }

YES: 功能拆解,

 
  1. function sendEmailToActiveClients(clients) {  //各個擊破,易於維護和複用

  2.  clients.filter(isActiveClient).forEach(email)

  3. }

  4.  

  5. function isActiveClient(client) {

  6.  const clientRecord = database.lookup(client)

  7.  return clientRecord.isActive()

  8. }

(6)優先使用函數式編程

NO: 使用for循環編程

 
  1. for(i = 1; i <= 10; i++) { // 一看到for循環讓人頓生不想看的情愫

  2.   a[i] = a[i] +1;

  3. }

YES:使用函數式編程

 
  1. let b = a.map(item => ++item) // 怎麼樣,是否是很好理解,就是把a的值每項加一賦值給b就能夠了。如今在javascript中幾乎全部的for循環均可以被map,filter,find,some,any,forEach等函數式編程取代。

(7) 函數中過多的採用if else ..

No: if else過多

 
  1. if (a === 1) {

  2.    ...

  3. } else if (a ===2) {

  4.    ...

  5. } else if (a === 3) {

  6.    ...

  7. } else {

  8.   ...

  9. }

YES: 可使用switch替代或用數組替代

 
  1. switch(a) {

  2.   case 1:

  3.           ....

  4.   case 2:

  5.           ....

  6.   case 3:

  7.           ....

  8.  default:

  9.       ....

  10. }

  11. Or

  12. let handler = {

  13.    1: () => {....},

  14.    2: () => {....}.

  15.    3: () => {....},

  16.    default: () => {....}

  17. }

  18.  

  19. handler[a]() || handler['default']()

3、儘可能使用ES6,有能夠能的話ES7中新語法(只羅列最經常使用的新語法,說實話,有些新語法不怎麼經常使用)

(1)儘可能使用箭頭函數

NO:採用傳統函數

 
  1. function foo() {

  2.  // code

  3. }

YES:使用箭頭函數

 
  1. let foo = () => {

  2.  // code

  3. }

(2)鏈接字符串

NO:採用傳統+號

 
  1. var message = 'Hello ' + name + ', it\'s ' + time + ' now'

YES:採用模板字符

 
  1. var message = `Hello ${name}, it's ${time} now`

(3) 使用解構賦值

NO:使用傳統賦值:

 
  1. var data = { name: 'dys', age: 1 };

  2. var name = data.name;

  3. var age = data.age;

  4.  

  5. var fullName = ['jackie', 'willen'];

  6. var firstName = fullName[0];

  7. var lastName = fullName[1];

YES:使用結構賦值:

 
  1. const data = {name:'dys', age:1};

  2. const {name, age} = data;   // 怎麼樣,是否是簡單明瞭

  3.  

  4. var fullName = ['jackie', 'willen'];

  5. const [firstName, lastName] = fullName;

(4) 儘可能使用類class

NO: 採用傳統的函數原型鏈實現繼承

 
  1. 典型的 ES5 的類(function)在繼承、構造和方法定義方面可讀性較差,當須要繼承時,優先選用 class。代碼太多,就省略了。

YES:採用ES6類實現繼承

 
  1. class Animal {

  2.  constructor(age) {

  3.    this.age = age

  4.  }

  5.  

  6.  move() {

  7.    /* ... */

  8.  }

  9. }

  10.  

  11. class Mammal extends Animal {

  12.  constructor(age, furColor) {

  13.    super(age)

  14.    this.furColor = furColor

  15.  }

  16.  

  17.  liveBirth() {

  18.    /* ... */

  19.  }

  20. }

  21.  

  22. class Human extends Mammal {

  23.  constructor(age, furColor, languageSpoken) {

  24.    super(age, furColor)

  25.    this.languageSpoken = languageSpoken

  26.  }

  27.  

  28.  speak() {

  29.    /* ... */

  30.  }

  31. }

先寫到這了,這是目前爲止發現的問題,這篇文章中並無徹底覆蓋到常見的寫代碼的很差的習慣,因此你若是覺的有須要補充的,均可以在文章下方評論,或者直接到個人Github的這篇文章中評論。對於有用的,都將補充到個人掘金和Github中去。同時,你若是覺的文章寫得還能夠,Please在個人Github中送上你寶貴的Star,你的Star是我繼續寫文章最大的動力。

 
  1. 注:除了上述這些人爲習慣以外,就像前面提到的,對於機械性的,你可使用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 (如何寫出優美的 JavaScript 代碼?)

END

做者:殷榮檜 https://juejin.im/post/5becf928f265da61380ec986

相關文章
相關標籤/搜索