代碼優化基本原則
- 易讀性優先
- 如果不是性能瓶頸,就不要爲了性能而改寫代碼
- 複雜性守恆原則:無論你怎麼寫代碼,複雜性都是不會消失的
推論:如果邏輯很複雜,那麼代碼看起來就應該是複雜的。如果邏輯很簡單,代碼看起來就應該是簡單的。
命名
程序員三大難題
- 變量命名
- 緩存失效
- 循環邊界
可見變量命名的重要性。
網上有很多命名規範,大家可以參考。這裏只講基本原則。
-
注意詞性
-
普通變量/屬性用「名詞」
var person = { name: 'Frank' } var student = { grade: 3, class: 2 }
-
bool變量/屬性用「形容詞」或者「be動詞」或者「情態動詞」或者「hasX」
var person = { dead: false, // 如果是形容詞,前面就沒必要加 is,比如isDead 就很廢話 canSpeak: true, //情態動詞有 can、should、will、need 等,情態動詞後面接動詞 isVip: true, // be 動詞有 is、was 等,後面一般接名詞 hasChildren: true, // has 加名詞 }
-
普通函數/方法用「動詞」開頭
var person = { run(){}, // 不及物動詞 drinkWater(){}, // 及物動詞 eat(foo){}, // 及物動詞加參數(參數是名詞) }
-
回調、鉤子函數用「介詞」開頭,或用「動詞的現在完成時態」
var person = { beforeDie(){}, afterDie(){}, // 或者 willDie(){} dead(){} // 這裏跟 bool 衝突,你只要不同時暴露 bool dead 和函數 dead 就行,怕衝突就用上面的 afterDie } button.addEventListener('click', onButtonClick) var component = { beforeCreate(){}, created(){}, beforeMount(){}, mounted(){}, beforeUpdate(){}, updated(){}, activated(){}, deactivated(){}, beforeDestroy(){}, destroyed(){}, errorCaptured(){} }
-
容易混淆的地方加前綴
div1.classList.add('active') // DOM 對象 div2.addClass('active') // jQuery 對象 不如改成 domDiv1 或 elDiv1.classList.add('active') $div2.addClass('active')
-
屬性訪問器函數可以用名詞
$div.text() // 其實是 $div.getText() $div.text('hi') // 其實是 $div.setText('hi')
-
-
注意一致性
介詞一致性
如果你使用了 before + after,那麼就在代碼的所有地方都堅持使用
如果你使用了 before + 完成時,那麼就堅持使用
如果你改來改去,就「不一致」了,不一致將導致「不可預測」順序一致性
比如 updateContainerWidth 和 updateHeightOfContainer 的順序就令人很彆扭,同樣會引發「不可預測」-
表裏一致性
函數名必須完美體現函數的功能,既不能多也不能少。
比如function getSongs(){ return $.get('/songs).then((response){ div.innerText = response.songs }) }
就違背了表裏一致性,getSongs 表示獲取歌曲,並沒有暗示這個函數會更新頁面,但是實際上函數更新了 div,這就是表裏不一,正確的寫法是
-
要麼糾正函數名
function getSongsAndUpdateDiv(){ return $.get('/songs).then((response){ div.innerText = response.songs }) }
-
要麼寫成兩個函數
function getSongs(){ return $.get('/songs) } function updateDiv(songs){ div.innerText = response.songs } getSongs().then((response)=>{ updateDiv(response.songs) })
-
時間一致性
有可能隨着代碼的變遷,一個變量的含義已經不同於它一開始的含義了,這個時候你需要及時改掉這個變量的名字。
這一條是最難做到的,因爲寫代碼容易,改代碼難。如果這個代碼組織得不好,很可能會出現牽一髮而動全身的情況(如全局變量就很難改)
改代碼
如果你的代碼有單元測試,那麼改起來就很放心。如果沒有單元測試,就需要用「小步快跑」的策略來修改。
小步快跑的意思是說,每次只修改一點點,測試通過後,再修改一點點,再測試,再修改一點點……如此反覆。
那麼如何修改一點點呢?《重構》這本書介紹了很多方法,但是講得挺囉嗦的,如果你有時間可以看看。
我這裏只說兩個經久不衰的方法。
一、使用函數來改代碼
步驟:
- 將一坨代碼放到一個函數裏
- 將代碼依賴的外部變量作爲參數
- 將代碼的輸出作爲函數的返回值
- 給函數取一個合適的名字
- 調用這個函數並傳入參數
- 這個函數裏的代碼如果超過 5 行,則依然有優化的空間,請回到第 1 步
二、使用對象來改代碼
如果使用了函數改造法改造後,發現有太多的小函數,則可以使用對象講這個函數串起來。
記得我們講過「this 是函數和對象的橋樑」嗎,我們會用 this 來串聯這個對象和所有函數。
最終代碼:http://js.jirengu.com/mimazaboke/1/edit?html,js,output
一些固定的套路
- 表驅動編程(《代碼大全》裏說的)
所有一一對應的關係都可以用表來做 - 自說明代碼(以 API 參數爲例)
把別人關心的東西放在顯眼的位置
bad smell(壞味道)
有些代碼可以用,但是很「臭」。
哪些代碼是有壞味道的
- 表裏不一的代碼
- 過時的註釋
- 邏輯很簡單,但是看起來很複雜的代碼
- 重複的代碼
- 相似的代碼
- 總是一起出現的代碼
破窗效應
此理論認爲環境中的不良現象如果被放任存在,會誘使人們仿效,甚至變本加厲。一幢有少許破窗的建築爲例,如果那些窗不被修理好,可能將會有破壞者破壞更多的窗戶。最終他們甚至會闖入建築內,如果發現無人居住,也許就在那裏定居或者縱火。一面牆,如果出現一些塗鴉沒有被清洗掉,很快的,牆上就佈滿了亂七八糟、不堪入目的東西;一條人行道有些許紙屑,不久後就會有更多垃圾,最終人們會視若理所當然地將垃圾順手丟棄在地上。這個現象,就是犯罪心理學中的破窗效應。
程序員要做到:只要是經過你手的代碼,都會比之前好一點。