想分享這個主題好久了,前些天和好朋友聊天的時候也提到過,我以爲新手最像新手的地方就在於新手考慮事情老是不夠全面,這體如今不少方面,好比設計上、技術選型上等等。今天我想分享給你們的是其中一個方面:異常處理。javascript
正好前些時間在手機上看到這麼一則小故事:前端
故事很可笑,可能你們看完都會以爲這羣程序員也太蠢了,連這種情況都沒有考慮到。其實在咱們工做中,也經常會出現只關注於正常流程處理的開發,而忽略一些非正常的流程的狀況。java
其實討論程序異常處理的話題比較大,我大概分紅三個方面來講,一個是程序的容錯,一個是業務上的容錯,另外一個是安全方面。node
程序的容錯是咱們寫代碼過程當中應該首要考慮的地方,好比當你要編寫一個模塊給別人使用時,若是沒有考慮到一些特殊狀況的輸入,可能模塊就沒法正常使用了。程序員
須要考慮容錯實際上是至關常見的場景,好比這裏我寫了一個好玩的「睡排序」,假設要給別人使用。面試
代碼比較簡單,你們可能也能看出裏面常見的幾個可能拋異常的地方。數據庫
首先是 data.whatever.inner.array
嵌套太深了,極可能讀取不到。這裏咱們經常使用缺省參數。如 ES6 中的寫法:json
第二個可能有問題的地方是 done 函數的調用,done 極可能外部沒有傳遞。這裏咱們經常使用短路運算的方式避免:後端
最後還有一個可能比較隱晦的地方,這裏是對數字進行排序,因此咱們應該考慮數組中每一項的類型是否合法,也就是類型校驗。類型校驗又是另一個話題,這裏只提一下對數字的校驗方法。跨域
你們用的比較多的應該是 isNaN 方法了,這是掛載在 window 上的方法。其實 ES6 也提供了一個 isNaN 方法,並掛載在了 Number 上,也就是 Number.isNaN。這二者的區別在於 window.isNaN 傳入 undefined、非空字符串等等其實並非 NaN 的參數時也返回 true,而 Number.isNaN 會首先判斷參數是否爲 number,因此咱們更推薦使用 Number.isNaN。
上面是一個比較簡單的例子,咱們針對性地提出了三點改進。有了這些改進,咱們的程序不會報錯,但在咱們日常的開發過程當中其實有些異常是不可避免的,咱們必需要針對可能出現的異常進行異常捕獲,說到這裏,你們可能最早想到的就是 try...catch 語句了。
try catch 是你們用的比較多的,這裏就不詳細展開說了,只提一下你們可能忽略的地方,就是 try catch 其實有三種形式:
finally 塊是無論異常與否都會進入的地方,咱們經常在這裏面作一些清理工做,在前端多是把一些變量置爲 null 避免內存泄露,後端多是關閉數據庫鏈接等等。
另外須要注意的是在 finally 中 return 的值將做爲 try catch 語句的總體返回值,無論在 try 或者 catch 中是否已經 return 了。
Promise 的異常捕獲是我在面試中比較常問的問題。Promise 是異步的,因此對其 try catch 是沒有做用的。
咱們通常使用 catch 方法去處理 Promise 的 reject 狀態,這裏須要知道的是 Promise 的 catch 方法只是一種特殊的 then 方法,catch 方法等同於調用 then 方法但把第一個參數置爲 undefined。
另外提一個新手可能會犯的錯誤,在 then 方法中的 onResolved 回調中拋出的異常只會讓返回的新 Promise 的狀態置爲 reject,而不會讓同一 then 方法中的 onRejected 方法執行。
async/await 是比 Promise 使用起來更方便也更容易理解的語法,它讓異步的執行有了同步的寫法。對於 async/await 的異常捕獲,我以爲只要理解兩個地方就行:
async 函數能夠當作普通函數調用,也可使用 await 表達式調用。看成爲普通函數調用時,如上所述,該函數返回一個 Promise,咱們使用 Promise 的異常捕獲便可:
當使用 await 表達式調用時,會使 async 函數暫停執行,等待表達式中的 Promise 解析完成後繼續執行 async 函數並返回解決結果。因此咱們可使用 try catch 進行捕獲:
其實以上說到的都算針對性的異常捕獲,但在實際開發中,咱們總會碰到咱們沒考慮到的程序異常,這裏可使用一個全局的異常捕獲方法進行處理。
在瀏覽器裏,提供了 window.onerror 事件,它會捕獲程序中出現的未被捕獲到的同步或異步的錯誤。
在事件處理函數中,能獲取到錯誤信息、當前 URL、代碼行數、列數等,很是詳細。
篇幅緣由,nodejs 的異常捕獲就不展開說了,在 process 上有三個事件:
說完程序裏的容錯,咱們接着說一下業務上的容錯。其實在本文開頭提到的例子就是一個典型的業務上沒有進行容錯的例子。其實業務上的容錯更多的是產品須要考慮的問題,但做爲開發,咱們也須要去理解業務,並能敏銳地發現一些業務中可能碰到的問題,避免開發到一半須要推翻重來。
代碼上拋出異常只會讓你的程序不可用,說白了用戶體驗就不會好。但一旦業務上發生問題,那麼影響的可能就是產品的業務線了。
舉個例子,咱們以前作了個項目,由於訂單的狀態太多,用戶會有不少不一樣的操做來使訂單轉移到不一樣的狀態,不一樣的狀態又會反應出不一樣的操做。最後就在不一樣狀態的切換中亂套了,致使上線以後收到了不少用戶狀態錯誤的反饋。
這跟開發每每沒有什麼太大的聯繫,而是對業務的理解。
最後跟你們聊一下安全。咱們說了這麼多,其實側重的都是正經常使用戶的使用,但在實際生產環境中,咱們必須還要考慮一些惡意的輸入。
篇幅緣由也不展開說了。。簡單提一下這幾種攻擊。
面試的時候我也會嘗試性問一下對方在安全方面考慮的問題。通常會從跨域開始問,而後天然而然提到 jsonp,jsonp 可讓咱們進行跨域請求,但其中是否會有安全問題?畢竟別人也能經過 jsonp 訪問你的網站了。
另外,當用戶登陸了 a.com 以後,打開了一個 evil.com,在 evil.com 裏向 a.com 發送請求,是會攜帶 a.com 的 cookie 的,包括 jsonp 請求,這裏的安全問題又要如何避免?
通常咱們會使用添加 token 的方式來防止 CSRF 攻擊,token 能夠隱藏在表單中,或者在請求頭裏攜帶,做爲一個合法請求的校驗。
這個多是前端接觸比較多的攻擊,通常分爲反射型的 XSS 攻擊和存儲型的 XSS 攻擊。區別在於存儲型的 XSS 攻擊會使惡意代碼存放在服務端,致使全部能看到這段代碼的用戶都受影響。
XSS 攻擊的誘導常見於一些惡意連接,當用於訪問一些奇怪的網址(好比郵箱裏的垃圾郵件、廣告連接),可能會跳轉到具備 XSS 漏洞的網站,從而引起安全問題。
通常咱們須要考慮對用戶的輸入進行合法校驗,包括前端和後端。
SQL 注入是後端同窗須要着重考慮的問題,對於一些小白代碼,特別常見於一些學校的管理後臺,極可能有 SQL 注入的漏洞,舉個最簡單的例子:
connection.query(`SELECT username from user where username = "${username}" and password = "${password}"`, function () {
// ...
})
複製代碼
若是後端對用戶輸入沒有進行任何過濾,直接是這麼校驗用戶登陸的話,那麼只要用戶猜出用戶名,如常見的 admin,而後使用 " or "1" = "1,就能正常登陸了。
說了這麼多,其實今天的主題就一個,那就是但願你們在之後的開發中,必定要多考慮異常的狀況,不要想固然地覺得用戶必定會按照正常的流程走進來。固然,這也須要產品同窗考慮各類可能出現的業務狀況、測試同窗進行各類 case 的測試。在你們共同的努力下,才能保證整個產品不會出現大的漏洞。
若是發現以上內容有任何不正確的地方,或者想一塊兒探討的,歡迎在評論區留言~