一個前端工程師看完《代碼大全》後的二三總結

NO IMAGE

題記: 書中自有黃金屋,書中自有顏如玉。 就好像 one piece ,等待著我們去發現,找到。不是所有的地方都有 one piece (書的每一章每一頁),也許在某處,有一段耐人尋味的話,等待著我們去發現。一些人可能錯過了,一些人可能找到了,這也許就是讀書的魅力吧。

寫在最前

讀了很多技術方面的書籍,這本 《代碼大全》 值得我去為它寫一篇總結,這也是我在掘金寫的第一篇書籍讀後感,我非常提倡多去閱讀書籍。

一個前端工程師看完《代碼大全》後的二三總結

因為,我個人覺得,每一個出書的人,在撰寫書的過程中,都投入了很多很多的時間和精力,他們會盡可能的把自己收穫的經驗、總結等都以儘可能的通俗易懂的方式在書中表達出來,這是一種本能的行為,就好像在遊戲中達成一個成就,出一本好書,可以說是每個出書人的初衷。

所以我給那些出書的人都點個贊吧,也許書中有很多地方我們覺得是照搬啥的,沒有營養啥的。但是以我的經驗來說,只要是公認的好書,基本上在某一章,甚至某一段,一定會帶給你柳暗花明或者說是茅塞頓開的驚喜感。就好比玩拳皇,一頓搖把加狂按,突然放出大招時的無以言表的驚喜和開心。。。嗯~~(從來沒按出來過)&¥&¥#。。。。。

開始我的表演

Just enough, 其他讚美的話就不多說了(讚美好書的詞語省略800字),下面準備步入正軌了。代碼大全這本書,我很久以前就買了,當時還沒入圈😂,看了一部分,也做了一些筆記,寫在了小本本上,自從入圈以來,就沒有看過了,最近幾天以前端工程師的身份重新完整的過了一遍,發現了新大陸,找到了幾處one piece。對溫故而知新的理念不能再贊同了,在此把我的一些總結和收穫分享出來,就像此書的一個讚譽那樣寫到:

每個程序員都改讀讀這本傑出的書籍。

趕快開始吧。。。

《代碼大全》總共有944頁內容,很實在,裡面涉及到的知識點很多,主體代碼是以Java和C++為主。代碼形式其實都不重要,重要的是要表達的思想。對於前端工程師來說,我總結了以下幾個方面的知識,開始吧。

條件語句switch用對了麼

很多時候,我們在用switch語句的時候,對如何使用default,並沒有一個明確的原則,有時候default下,什麼都不執行,有時候default下,會去執行最後一種可能。還有有時候case的情況很多,我們並沒有在意排序問題。等等其他,其實這在編程中都是一種不好的行為。那麼來,怎樣才是正確編寫switch語句的姿勢呢?請看如下:

tip1——讓default幹它該乾的事情

原則上,default下,不要寫最後一種可能,因為default設計出來的目的,其實是為了捕捉錯誤的。所以業務中出現的所有情況都應該用case去捕捉,這也體現了使用case後面的label去說明這是哪種情況的作用。而default後面無法加label說明,也就失去了對可能情況的說明。舉個栗子,在前端中,如果對請求返回的狀態碼進行判斷,可以寫成如下形式:

switch(code) {
case: '1':
console.log('成功')
break
case: '0':
console.log('失敗')
break
default:
dealError()
}

從上面代碼可以看出,default不要做任何有正常case的操作,它專門用來和檢測和處理錯誤,當然如果業務中把某些case都當成錯誤的話,也可以統一寫到default中。

tip2-讓case變的簡潔、有序和高效

1: 如果case的情況很多,那就要按照出現頻率去從上到下排列,這樣switch語句代碼的整體效率會提高。

2: 如果case的一個情況,語句太多,那就果斷封裝成子程序來調用。避免在一個case下寫很多語句,讓case變得難以理解。

如何判斷代碼的複雜度

大家可能會本能的想到大O方法啥的,對前端來說,大O那種模式不適用,大O是衡量算法的複雜度。全全裡面提到了一個很有趣的判斷複雜度的方法,叫做 Tom McCabe 方法,該方法很簡單,通過計算函數中 決策點 的數量來衡量複雜度。下面是一種計算決策點(結合前端)的方法:

  1. 1開始,一直往下通過函數
  2. 一旦遇到if while for else或者帶有循環的高階函數,比如forEach map等就加1
  3. 給case語句中的每一種情況都加1

比如下面代碼:

function fun(arr, n) {
let len = arr.length
for (let i = 0; i < len; i++) {
if (arr[i] == n) {
// todo...
} else {
// todo...
}
}
}

按照Tom McCabe方法來計算複雜度,那麼這個fun函數的決策點數量是 3 。知道決策點數量後,怎麼知道其度量結果呢?這裡有一個數值區間判斷:

數量區間度量結果
0-5這個函數可能還不錯
6-10得想辦法簡化這個函數了
10+把這個函數的某一部分拆分成另一個函數並調用他

從上面的判斷依據可以看出,我上面寫的代碼,數量是3,可以稱為函數還不錯。我個人覺得這個判斷方法還是很科學和簡單的,可以作為平時寫代碼時判斷函數需不需要重構的一個考慮點,畢竟一個函數,一眼掃去,決策點數量大致就知道是多少了,很好計算。

你心中的抽象和封裝以及模塊是什麼?

怎麼說呢,其實有些時候,我們不知道,不代表我們不會,很多時候是這種情況:我真的看過這方面的知識或者我確實真的研究過這些,但是確實是忘了😂。所以此刻,你看到這裡的時候,就贊贊收藏吧(嘻嘻)。

我覺得這種偏概念的東西,每個人都可以回答的不同,但是隻要你真正理解了其中的本質或者說是一種答案,其實這就夠了。這裡我說一下我自己對抽象和封裝以及模塊的看法。

抽象在我眼裡,其實是把混亂變成有序,把零散變成整體。我們經常說,要把業務代碼抽象成組件,做成組件化。其實就是把零散變成整體,把混亂變成有序的過程。舉個荔枝:

比如說我們項目中還沒有做組件化,彈窗這個功能,還是各種頁面裡寫自己的彈窗代碼。現在我們需要重構,把這個零散和混亂的彈窗,抽象成一個整體有序的彈窗組件。那麼,我們來看看原來的彈窗代碼,發現以下問題:

  1. 代碼重複,同時零散分佈在各個頁面中。
  2. 代碼混亂,沒有統一的入口,沒有統一的顯示邏輯。

如何解決呢,這個時候就需要對彈窗進行抽象了,如何抽象,一個辦法,就是解決掉重複和混亂,如果解決掉了,就可以表示抽象已經初見成效了。

這裡提一下ADT,這是面對對象的編程模式中的類的基礎,叫 Abstract Data Type ,也就是抽象數據類型,但是我不想用這個,這裡我借鑑DOMBOM形式,我把它叫做 ADM。 全稱:Abstract Data Model 也就是抽象數據模型。不知道可不可以這樣玩,但是我覺得問題不大,嘻嘻。沒有意見,那下面我繼續操作了。

我們需要把彈窗可能用到的數據全部收集起來,然後將這些數據歸屬於一個對象模型,為了形象比喻,我把它叫做彈窗人,擁有生命。這個彈窗人的實體是由這些數據組成的,OK,這樣就把之前零散的數據全部集合在一個實體上了,也就是把零散變成整體。請看下面這句話:

沒有封裝時,抽象往往很容易打破。

如果我只是抽象了,把零散變成整體了,但是卻沒有把混亂變成有序,那麼抽象就會被打破,就好比彈窗人擁有的數據,這裡我花式比喻一下。比如彈窗人擁有100萬,關鍵100萬還是公開的,結果悲劇了。

很多人都想向他借錢,如果彈窗人事先不指定好借錢的方式。那麼來就會很刺激了,朋友A微信問他借錢,朋友B支付寶向他收款,社會混子直接電話向他威脅借錢,更有牛逼的,直接強行把他綁架了,然後刀光劍影伺候。彈窗人心裡苦啊,難受。

終於彈窗人大悟了,事先指定了借錢的方式,只能先通過短信,其他借錢方式一律拒絕,並且要經過他的同意,同時堅決不公開自己的財產。從此以後,沒有人知道彈窗人有多少錢,還以為是個窮逼(彈窗人是程序員),就算想借錢,也只能通過短信告訴彈窗人我想借多少錢,然後同意後,才借給你。從此,彈窗人過上了幸福快樂的生活了。

上面是即興寫出來的栗子。忽然發現我還挺有想象力的。給自己 0110 0110 0110,其實你讀完大概就知道我想表達什麼思想了。這就是封裝的目的,不僅是前端領域封裝的目的,同時也是所有面對對象領域封裝的目的。如果有例外,,捂嘴,,沒有。。

從上面我們可以看出,抽象和封裝存在密不可分的聯繫。仔細去體味上面的故事,你會發現我們在讓混亂變成有序的的時候,其實是在讓可訪問性變得儘可能的低,也就是封裝本身的原則:

封裝的原則是讓可訪問性儘可能的低。

很多人雖然知道封裝是為了降低可訪問性,但是卻不知道為什麼,可能也知道為什麼吧?這裡中斷一下,跳個番外。

在貴(程序員)圈裡有個很好玩的現象,那就是我知道某一個知識的目的,但是卻說不清原理,或者說我也查過,試圖去了解這個原理,但是總是會有一種朦朦朧朧的不懂感圍繞著自己。然後心裡 ob :先就這樣吧,後面再說😂。然後就不能透徹的理解。我咋知道這些的,因為我有時也有這種感覺,感覺是無法避免的。能做的就是在日後別忘了把當時朦朦朧朧的困惑解決掉。

中斷結束,繼續胡謅🙂,我有多少錢你不能知道,知道的話,我的錢就會變得不安全。你只能通過固定的渠道來訪問我,還不能直接訪問數據,需要經過我的同意,才能進行相應的操作。其實可訪問性這個已經解釋的很透徹了。好了,不解釋了。


那麼前端的模塊化是什麼意識呢 ?

其實完全可以理解為組件化。模塊化,顧名思義想表達的就是想要整體的意識,既然整體,那就要封裝,封裝前就需要進行抽象,然後又迴歸本質。那麼目前前端是怎樣實現模塊化的呢?

JavaScript 是以面向對象為基礎的編程語言,至少有句話說的是:在JavaScript中,一切皆對象,其實可以這麼說,但是JavaScript有個很尷尬的一點是,沒有類,雖然現在有類,但也只是一種語法糖。這裡不考慮TypeScript。那它怎麼實現模塊化呢?

實現編程語言中的類的抽象模型,也就是我上面說的ADM。在我看來,在不使用class語法糖的情況下,前端的模塊化的實現原理就是通過使用JavaScript的私有變量特性和閉包特性來實現模塊化。ES5的時候,JS是沒有塊級作用域的,只有全局變量和私有變量,如果連私有變量也沒有的話,那JavaScrript也就GG了,所以我很好奇當初設計JS的時候,都採用了類C的語言設計風格,為什麼沒有實現塊級作用域呢,是不是當時覺得JS並不需要這麼多功能啊😂。既然有私有變量,那麼就會有私有屬性和私有方法,畢竟本質上都是變量。具體代碼我就不貼了,寫個偽代碼吧:

function fun() {
v1 是私有屬性
f1 是私有方法
// 返回一個對象
return {
v2 是共有屬性
f2 是共有方法 {
共有方法裡面可以訪問和修改 私有屬性和私有方法
處理 v1
處理 f1
}    
}
}

如何編寫高質量的函數

全全(指代碼大全,下同)裡寫的是如何編寫高質量的子程序,其實JS函數就是子程序(99%正確)。全全裡說的高質量的一些總結有些不適用JS。 這裡我融合一下自己的一些經驗和總結。請往下看:

有沒有想過為什麼要發明函數這個東西?

作為前端工程師,對於這個問題,還是10分重視的。頭偏向45度方向,在腦袋中想個10秒鐘,可能還會眨幾下眼睛,不要問我為什麼知道你的狀態,因為XX。其實大家都能說出一些自己的看法,不需要答案,但是我還是想給一個看起來逼格很高的answer。那就是:

函數是迄今為止發明出來的用於節約空間和提高性能的最重要的手段。 注意,沒有之一。那麼我們現在可以確定的是: 它是用來節約空間和提高性能的。 這樣的話,如果我們不去優化函數,寫出高質量的函數,豈不是失去了它存在的意義。現在面臨的最關鍵的問題就是:如何去優化函數,寫出高質量的函數?

if (str === 'hello world') {
console.log('hello world')
} else {
console.log('666')
}

上面是一段代碼,如果在其他地方要用,就必須在用的地方把上面這段代碼原封不動的重複編寫一遍,但是有了函數,就可以將其放到函數內,然後通過函數print(str)來調用,在其他地方只需要寫這一行就可以了,優點很明顯。

如何寫出高質量的函數。其實就這一點就可以寫一篇文章了,這裡我不詳細說明闡述了,隨便提幾點吧,高質量的具體實現以後再說😂。

從娃娃抓起,起一個好名字很重要

不能輸出起跑線上,考慮的點有以下幾點:

  1. 名字要能清晰的描述此函數的目的
  2. 統一好書寫形式。比如統一使用下劃線、駝峰等命名形式
  3. 統一好單詞組成形式,是名詞+動詞,還是動詞+名詞

優雅的傳遞和處理參數

  1. 參數較多,可以使用參數對象模式
  2. 參數不固定,比如動態參數,可以使用ES6的擴展符
  3. 使用ES6的參數默認值來處理參數默認取值問題

設計函數時的一些考慮

  1. 避免函數代碼重複
  2. 縮小函數代碼規模
  3. 增加函數魯棒性
  4. 註釋風格要統一
  5. 儘可能的保持pure特性,也就是冪等性,這樣可以最大限度的防止後期的各種莫名其妙的bug

關於跨度和生存時間

變量有一個自己的屬性,叫存活時間,在編程語言中,要儘可能的縮短變量的存活時間。這樣的話,如果我們用跨度和生存時間的概念來看全局變量,就會發現為什麼要避免使用全局變量,因為全局變量的跨度和生存時間都很長。

彩蛋:JS 為什麼要避免全局變量,其實一個主要原因是 JS 的代碼執行,是由裡向外的,如果是全局變量的話,那麼這個查找的時間就比較長,所以在 JQuery 等源碼中,直接將 window 當成參數傳到函數中,是有它的道理的,可以提高代碼性能。

關於布爾表達式的一個鮮為人知的點

看如下代碼

let i = true 
if (i = true) {
// todo...
}

可以看到,如果你在業務代碼中,不小心少寫=號,變成上面這段代碼,那麼它是不會報錯的,但是要是寫成這種形式:

let i = true
if (true = i) {
// todo...
}

就會報錯了,因為如果表達式左側不是變量的話,解釋器會報錯。所以在JS編程中,可以把常量放在等號左側。這是一個很小眾的 tip,而且在前端來說,給項目加個 eslint 就可以完全避免這種錯誤了,如果沒有寫 eslint,可以考慮一下這個 tip,如果用了代碼檢查工具,那就當下酒菜吧。

來點佛學的知識

如何修改bug、代碼缺陷

這個。。。五彩臉?對於及時保存源碼等這種建議就不提了,畢竟 git 操作穩的一比,提一下其他方面的雞湯吧:

  1. 放鬆一下:嗯。。。。。。
  2. 修改代碼時一定要有恰當的理由。理由要充分,有理有據。
  3. 一次只做一個改動

備註

  1. 設計模式這個就不總結了,東西很多,需要拿出來單獨說。
  2. 一些系統級的總結也不說了,和前端關係不大。
  3. 對於遞歸的注意事項也不說了,前端用到遞歸的情況不多。
  4. 對於偽代碼編程,以後單獨提
  5. 對於編寫高質量的函數,總結的簡單,因為涉及到的東西很多,這裡我更想提的是最本質的一些知識,比如被創造出來的目的,有時候你寫了很多函數,都沒有想過這個問題,現在看到了,心中會有種豁然開朗的感覺吧。

文末的可愛聲明: 如果轉發或者引用,請貼上原鏈接。本文就好比一本書,可能有一些地方對於你來說是沒有營養,但是隻要有那麼一段,或者一句讓你感到驚喜的地方,我就很開心了。文章可能有一些錯誤,歡迎評論指出,也歡迎一起討論。文章可能寫的不夠好,還請多多包涵。多一點貢獻,多一分開心~

相關文章

初中高級的git和gerrit技巧【大型項目實戰總結&&CR經驗】

不敢閱讀npm包源碼?帶你揭祕taroinit背後的哲學

探索Python來反補JavaScript,帶你CrossFire——JS數據類型的奧祕

前端獵奇系列之探索Python來反補JavaScript——上篇