20條常見的編碼陷阱 你中槍了沒?

20條常見的編碼陷阱 你中槍了沒?

發表於2012-11-19 08:4328141次閱讀| 來源CSDN編譯3 條評論| 作者張紅月

摘要:在平時的編程工作中,大多數程序員很少會關心細節問題,本文作者跨越多個語言,爲大家總結了20條常規陷阱,並提供了很好的解決方案,供大家參考學習。

不管你現在的編程技能有多麼的高超,曾經你也是個亦步亦趨,不斷的學習的初學者。在編程這條曲折的道路上,我想你肯定犯過一些低級的錯誤、遇見過一些普通的編碼陷阱。本文作者跨越多個語言,爲大家總結了20條常規陷阱,並提供瞭解決方案。

JavaScript篇

1.不必要的DOM操作

例如下面這段代碼:

  1. // anti-pattern  
  2. for (var i = 0; i < 100; i++){  
  3.    var li = $("<li>").html("This is list item #" + (i+1));  
  4.    $("#someUL").append(li);  

這段代碼對DOM進行了100次修改,並且創建了100個不必要的jQuery對象。正確的做法是使用一個文檔片段,或者創建一個字符串,把100個<li>元素賦給該字符串。然後附加到HTML中。這樣就只需運行DOM一次,代碼如下:

  1. var liststring = "";  
  2. for (var i = 100; i > 0; i--){  
  3.    liststring += "<li>This is list item #" + (99- i);  
  4. }  
  5. document.getElementById("someUL").innerHTML(liststring); 

正如上面所描述的一樣,下面再提供一個方式,使用數組:

  1. var liststring = "<li>" 
  2. var lis = [];  
  3. for (var i = 100; i > 0; i--){  
  4.    lis.push("This is list item #" + (99- i));  
  5. }  
  6. liststring += lis.join("</li><li>") + "</li>";  
  7. document.getElementById("someUL").innerHTML(liststring); 

這是在JavaScript創建重複HTML最快最簡單的方法,無需使用模板庫或框架。

2.不一致的變量名和函數名

這個問題是非常重要的,尤其當你在別人的代碼上工作時,一定要保持標識符(變量名和函數名)一致,例如下面這段代碼:

  1. var foo = "bar";  
  2. var plant = "green";  
  3. var car = "red"

通常,人們並不會設置變量名叫Something,這涉及到命名規則問題,命名應清晰明瞭,一目瞭然。很多編程語言地變量命名都使用大寫。

下面是對函數的命名:

  1. function subtractFive(number){  
  2.    return number - 5;  

語法結構清晰並且能起到解釋性功能。

例如想要對給定的數字加5,仍採用上述命名模式,比如:

  1. function addFive(number){  
  2.    return number + 5;  

有時,你會根據返回值命名,例如該函數要返回一個HTML字符串,那麼可以命名爲getTweetHTML(),如果函數只是做一些操作,無需返回值,那麼可以在前面加一個do前綴。例如doFetchTweets()。

構造函數通常會遵循類原則,大寫第一個字母:

  1. function Dog(color){  
  2.    this.color = color;  

命名應帶有描述性,比如操作型的函數在前面加do,另外要具備可讀性和提示性。

3.在for...Loops中使用hasOwnProperty()方法

JavaScript數組是沒有關聯的,可以把它當做哈希表,使用循環來遍歷對象屬性:

  1. for (var prop in someObject) {  
  2.     alert(someObject[prop]); // alert's value of property  

然而,存在的問題是for...in loop是在對象屬性鏈上遍歷每個枚舉類型的屬性,如果你只想使用對象實際擁有的屬性,這可能有問題的。那怎麼解決呢?你可以使用hasOwnProperty()方法。代碼如下:

  1. for (var prop in someObject) {  
  2.     if (someObject.hasOwnProperty(prop)) {  
  3.         alert(someObject[prop]); // alert's value of property  
  4.     }  

4.比較布爾值

把布爾值作爲條件進行比較,其實這是在浪費電腦的計算時間。看下面這個例子吧:

  1. if (foo == true) {  
  2.     // do something for true  
  3. else {  
  4.     // do something for false  

其實foo==true這個比較完全是多餘的,因爲foo已經是布爾類型。直接這樣寫就行:

  1. if (foo) {  
  2.     // do something for true  
  3. else {  
  4.     // do something for false  

又或者這樣寫:

  1. if (!foo) {  
  2.     // do something if foo is false  
  3. else {  
  4.     // do something if foo is true  

5.事件綁定

在JavaScript中,事件是個複雜的問題。事件冒泡(event bubbling)和委託正在取代內聯事件(inline onclick)操作(一些特殊的“初始頁”除外)。

假設你有一個圖片網格,需要啓動一個modal lightbox窗口。千萬不要採取下面的做法,示例採用的是jQuery,如果你使用相似的庫或者其他,冒泡機制也同樣適合傳統的JavaScript。

相關的HTML代碼:

  1. <div id="grid-container"> 
  2.    <a href="someimage.jpg"><img src="someimage-thumb.jpg"></a> 
  3.    <a href="someimage.jpg"><img src="someimage-thumb.jpg"></a> 
  4.    <a href="someimage.jpg"><img src="someimage-thumb.jpg"></a> 
  5.    ...  
  6. </div> 

不好的JavaScript寫法:

  1. $('a').on('click'function() {  
  2.    callLightbox(this);  
  3. }); 

這段代碼假設調用lightbox,裏面傳遞一個anchor元素並且引用全屏圖片。與其綁定每個anchor元素還不如直接使用#grid-container元素。

  1. $("#grid-container").on("click""a"function(event) {  
  2.    callLightbox(event.target);  
  3. }); 

在這段代碼中,this和event.target都表示anchor元素。同樣你也可以在任何父元素上使用。只要保證所定義的元素是事件目標就行(event's target)。

6.避免三元冗餘

在JavaScript和PHP中,過度使用三元語句是很常見的事情:

  1. // javascript  
  2. return foo.toString() !== "" ? true : false
  1. // php  
  2. return (something()) ? true : false; 

條件判斷的返回值永遠只有false和true,言外之意就是你無需把true和false顯示添加到三元運算中。相反,你只需簡單的返回條件:

  1. // javascript  
  2. return foo.toString() !== ""
  1. // php  
  2. return something(); 

PHP篇

7.適當的時候使用三元操作

If...else語句是大多數語言的重要組成部分。但有些簡單的事情,比如根據條件進行賦值,你很有可能會這樣寫:

  1. if ($greeting)  
  2. {  
  3.     $post->message = 'Hello';  
  4. }  
  5. else 
  6. {  
  7.     $post->message = 'Goodbye';  

其實使用三元操作只需一行代碼就可以搞定,並保持了良好的可讀性:

  1. $post->message = $greeting ? 'Hello' : 'Goodbye'

8.拋出異常,而不是採用盜夢空間式的嵌套(Inception-Style Nesting)

多層次的嵌套是醜陋的、難以維護和不可讀的。下面的代碼是個簡單的例子,但是隨着時間的推移會變得更糟:

  1. // anti-pattern  
  2. $error_message = null;  
  3. if ($this->form_validation->run())  
  4. {  
  5.    if ($this->upload->do_upload())  
  6.    {  
  7.       $image = $this->upload->get_info();  
  8.       if ( ! $this->image->create_thumbnail($image['file_name'], 300, 150))  
  9.       {  
  10.          $error_message = 'There was an error creating the thumbnail.';  
  11.       }  
  12.    }  
  13.    else 
  14.    {  
  15.       $error_message = 'There was an error uploading the image.';  
  16.    }  
  17. }  
  18. else 
  19. {  
  20.    $error_message = $this->form_validation->error_string();  
  21. }  
  22. // Show error messages  
  23. if ($error_message !== null)  
  24. {  
  25.    $this->load->view('form'array(  
  26.       'error' => $error_message,  
  27.    ));  
  28. }  
  29. // Save the page  
  30. else 
  31. {  
  32.    $some_data['image'] = $image['file_name'];  
  33.    $this->some_model->save($some_data);  

如此凌亂的代碼,是否該整理下呢。建議大家使用異常這個清潔劑:

  1. try  
  2. {  
  3.    if ( ! $this->form_validation->run())  
  4.    {  
  5.       throw new Exception($this->form_validation->error_string());  
  6.    }  
  7.    if ( ! $this->upload->do_upload())  
  8.    {  
  9.       throw new Exception('There was an error uploading the image.');  
  10.    }  
  11.    $image = $this->upload->get_info();  
  12.    if ( ! $this->image->create_thumbnail($image['file_name'], 300, 150))  
  13.    {  
  14.       throw new Exception('There was an error creating the thumbnail.');  
  15.    }  
  16. }  
  17. // Show error messages  
  18. catch (Exception $e)  
  19. {  
  20.    $this->load->view('form'array(  
  21.       'error' => $e->getMessage(),  
  22.    ));  
  23.    // Stop method execution with return, or use exit  
  24.    return;  
  25. }  
  26. // Got this far, must not have any trouble  
  27. $some_data['image'] = $image['file_name'];  
  28. $this->some_model->save($some_data); 

雖然代碼行數並未改變,但它擁有更好的可維護性和可讀性。儘量保持代碼簡單。

9.False——Happy方法

Ruby或Python開發者常常關注一些微小的異常,這是相當不錯的事情。如果有地方出錯就會拋出異常並且你會立即知道問題所在。

在PHP中,特別是使用比較老的框架,如CodeIgniter,與拋出異常相比,它僅僅返回一個flase值,並且把錯誤字符串分配給其他一些屬性。這就驅使你使用get_error()方法。

Exception-happy遠遠好於false-happy。如果代碼裏面存在錯誤(例如不能連上S3下載圖片,或者值爲空等),然後拋出一個異常,你也可以通過繼承Exception類來拋出特定的異常類型,例如:

  1. class CustomException extends Exception {} 

拋出自定義類型異常會讓調試變得更加容易。

10.Use Guard Clauses

使用if語句控制函數或方法的執行路徑是很常見的事情,如果if條件爲true就執行if裏面的代碼,否則就執行else裏面的代碼。例如下面這段代碼:

  1. function someFunction($param) {  
  2.     if ($param == 'OK') {  
  3.        $this->doSomething();  
  4.        return true;  
  5.     } else {  
  6.        return false;  
  7.     }  

這是很常見的意大利麪條式的代碼,通過轉換條件對上述代碼進行優化,不僅可以增加其可讀性,看起來還會更加簡單,如下:

  1. function someFunction($param) {  
  2.     if ($param != 'OK'return false;  
  3.     $this->doSomething();  
  4.     return true;  

11.使用While進行簡單的迭代

使用for進行循環是很常見的事情:

  1. for (var i = 0; i < x; i++) {  
  2.     ...  

當然,for循環也有許多優勢,但是對於一些的循環,使用while或許會更好:

  1. var i = x;  
  2. while (i--) {  
  3.     ...  

12.保持方法可維護性

讓我們來看一下這個方法:

  1. class SomeClass {  
  2.    function monsterMethod() {  
  3.       if($weArePilots) {  
  4.          $this->goAndDressUp();  
  5.          $this->washYourTeeth();  
  6.          $this->cleanYourWeapon();  
  7.          $this->takeYourHelmet();  
  8.          if($this->helmetDoesNotFit())  
  9.             $this->takeAHat();  
  10.          else 
  11.             $this->installHelmet();  
  12.          $this->chekcYourKnife();  
  13.          if($this->myAirplain() == "F22")  
  14.             $this->goToArmyAirport();  
  15.          else 
  16.             $this->goToCivilianAirport();  
  17.          $this->aim();  
  18.          $this->prepare();  
  19.          $this->fire();  
  20.       }  
  21.    }  

再看如下代碼:

  1. class SomeClass {  
  2.    function monsterMethod() {  
  3.       if($weArePilots) {  
  4.          $this->prepareYourself();  
  5.          $this->tryHelmet();  
  6.          $this->findYourAirport();  
  7.          $this->fightEnemy();  
  8.       }  
  9.    }  
  10.    private function prepareYourself() {  
  11.       $this->goAndDressUp();  
  12.       $this->washYourTeeth();  
  13.       $this->cleanYourWeapon();  
  14.       $this->chekcYourKnife();  
  15.    }  
  16.    private function tryHelmet() {  
  17.       $this->takeYourHelmet();  
  18.       if($this->helmetDoesNotFit())  
  19.          $this->takeAHat();  
  20.       else 
  21.          $this->installHelmet();  
  22.    }  
  23.    private function findYourAirport() {  
  24.       if($this->myAirplain() == "F22")  
  25.          $this->goToArmyAirport();  
  26.       else 
  27.          $this->goToCivilianAirport();  
  28.    }  
  29.    private function fightEnemy() {  
  30.       $this->aim();  
  31.       $this->prepare();  
  32.       $this->fire();  
  33.    }  

對比兩段代碼,第二段代碼更加簡潔、可讀和可維護。

13.避免深層嵌套

太多層的嵌套會讓代碼很難閱讀、理解和維護。看看下面的代碼:

  1. function doSomething() {  
  2.     if ($someCondition) {  
  3.         if ($someOtherCondition) {  
  4.             if ($yetSomeOtherCondition) {  
  5.                 doSomethingSpecial();  
  6.             }  
  7.             doSomethingElse();  
  8.         }  
  9.     }  

條件裏面又嵌套多個條件,通過轉換條件,我們對代碼進行了調整:

  1. function doSomething() {  
  2.     if (!$someCondition) {  
  3.         return false;  
  4.     }  
  5.     if (!$someOtherCondition) {  
  6.         return false;  
  7.     }  
  8.     if ($yetSomeOtherCondition) {  
  9.         doSomethingSpecial();  
  10.     }  
  11.     doSomethingElse();  
  12. }  

相對於前面的代碼,這段代碼簡潔了很多,並且所實現的功能也是一樣的。

當你在if裏面使用嵌套,請仔細檢查代碼,裏面可能同時執行多個方法,例如下面這段代碼:

  1. function someFunc() {  
  2.    if($oneThing) {  
  3.       $this->doSomething();  
  4.       if($anotherThing)  
  5.          $this->doSomethingElse();  
  6.    }  

這種情況下,可以把嵌套代碼提取出來:

  1. function someFunc() {  
  2.    if($oneThing) {  
  3.       $this->doSomething();  
  4.       $this->doAnotherThing($anotherThing);  
  5.    }  
  6. }  
  7. private doAnotherThing($anotherThing) {  
  8.    if($anotherThing)  
  9.       $this->doSomethingElse();  

14.避免使用匿名數字和字符串(Avoid Magic Numbers and Strings)

使用匿名數字和字符串是有害無益的,在代碼裏定義需要使用的變量和常量。比如下面這段代碼:

  1. function someFunct() {  
  2.    $this->order->set(23);  
  3.    $this->order->addProduct('superComputer');  
  4.    $this->shoppingList->add('superComputer');  

給23和“superComputer”賦予相應意義的變量名:

  1. function someFunct() {  
  2.    $orderId = 23;  
  3.    $selectedProductName = 'superComputer';  
  4.    $this->order->set($orderId);  
  5.    $this->order->addProduct($selectedProductName);  
  6.    $this->shoppingList->add($selectedProductName);  

可能會有人認爲,一些無意義的變量儘量少定義,雖然它們對性能的影響是微不足道的。但可讀性永遠處於優先地位。請記住:不要隨便優化性能,除非你知道爲什麼。

15.使用Built-in數組函數

使用built-in函數來代替foreach()

差的代碼:

  1. foreach (&$myArray as $key =>$element) {  
  2.    if ($element > 5) unset ($myArray[$key]);  

改進後的代碼:

  1. $myArray = array_filter($myArrayfunction ($element) { return $element <= 5;});  

PHP裏面提供了許多數組方法。起初會混淆,但是試着花時間好好學學它們。

16.不要過度使用變量

大家在開發過程中很容易使用變量,但請記住,變量是需要存儲在內存中的。看下面這段代碼:

  1. public function get_posts() {  
  2.    $query = $this->db->get('posts');  
  3.    $result = $query->result();  
  4.    return $result;  

$result變量其實是不需要的。

  1. public function get_posts() {  
  2.    $query = $this->db->get('posts');  
  3.    return $query->result();  

雖然這些差別都是微不足道的,但對於養成良好的編碼習慣還是 很重要的。

通用篇

17.依賴數據庫引擎

使用數據庫來專門處理數據會讓你的程序更高效。

例如,在大多數情況下,你可以避免冗餘的數據查詢。大多數的plug-and-play用戶管理腳本在用戶註冊時都使用了兩次數據查詢:先檢查用戶名/郵件是否存在,另外再把用戶信息插入到數據庫中。一個比較好的做法是在數據庫中設置username字段爲UNIQUE,然後你可以利用本地的MySQL函數來檢查用戶名是否存在,然後添加進去。

18.正確命名變量

使用x、y、z命名變量的時代已經結束(除非是處理一個座標系統)。變量是你邏輯代碼的重要組成部分。不想鍵入長名字嗎?獲取一個好的IDE吧,使用IDE只需一眨眼的功夫就可以完成變量命名。

19.方法表示動作

見名知意,看到方法名字就知道它執行了哪些動作。使用一個短的,但具有描述性的範圍命名(例如:public methods即可這樣命名);使用一個長的名字,並且可以更加詳細的描述(例如:定義private/protected methods)。這樣會讓你的代碼更加可讀可寫。

當然也要避免用非英語來進行命名。例如使用“做些什麼()”或者делатьчтото()命名,簡直是糟透了的命名。對於其他程序員來說,真的很難理解。尤其是在一個團隊裏,請記住,讓你命名更加規範些吧!

20.結構的定義

最後,我們來說一下代碼結構,從可讀性和可維護性來講,代碼結構也是相當重要的,下面我們從兩方面來講:

  • 首行縮進4字節或2個標籤寬度。
  • 設置合理的線寬(line-width)並且保持。一行只有40個字節?我們已經不是70年代的人了。一行限制在120個字節,並且在屏幕上放一個標籤,並且驅使IDE保持。

結論

發生錯誤不要緊,關鍵是要總結錯誤,並且從中吸取教訓,只有不斷總結和學習,才能讓你的編程之路走的更遠。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章