文章轉自豆皮范兒——防御性編程
引子
一個測試工程師走進一家酒吧,要了一杯啤酒;
一個測試工程師走進一家酒吧,要了一杯咖啡;
一個測試工程師走進一家酒吧,要了-1杯啤酒;
一個測試工程師走進一家酒吧,要了一份asdfQwer@24dg!&*(@;
一個測試工程師走進一家酒吧,什么也沒要;
一個測試工程師走進一家酒吧,又走出去又進來又出去又進來又出去,最后在外面把老板打了一頓;
一個測試工程師走進一家酒吧,要了NaN杯Null;
一個測試工程師沖進一家酒吧,要了500噸啤酒;
一個測試工程師把酒吧拆了;
一個測試工程師化裝成老板走進一家酒吧,要了500杯啤酒並且不付錢;
一萬個測試工程師在酒吧門外呼嘯而過;
測試工程師們滿意地離開了酒吧。
然后一名顧客點了一份炒飯,酒吧炸了。
今天的主題就是 如何避免酒吧爆炸 防御性編程
防御性編程(Defensive programming)是防御式設計的一種具體體現,它是為了保證,對程序的不可預見的使用,不會造成程序功能上的損壞。 它可以被看作是為了減少或消除墨菲定律效力的想法。
保護我們的代碼遠離來自“外部”的無效數據,無論這個“外部”的概念被定位為什么。
以前端為例,大概包括以下方面
-
后端接口
-
字段變化、增減
-
數據結構變化
-
各種
null、undefined之類的 -
網絡環境造成的問題
-
用戶輸入和操作
-
輸入預期之外的內容
-
非常規操作,如快速點擊
-
非常規環境,如慢網速環境
- 模塊/組件以外的數據
- ...
防御場景舉例(以JavaScript為例)
空值造成的相關問題
Uncaught TypeError: Cannot read property 'b' of undefined
Uncaught TypeError: fn is not a function
要時刻注意外部變量是否有值,下面是處理空值的幾種方法
-
初始化值
// 邏輯或(||)操作符,會在左操作數為 假值 時返回右側操作數。
const newData = data || {}
// 空值合並運算符(??) 是一個邏輯運算符。當左側操作數為 null 或 undefined 時,其返回右側的操作數。否則返回左側的操作數。
const newDataAlt = data ?? {}
console.log(data.id)
-
使用 函數默認參數
函數默認參數在沒有值或undefined被傳入時使用默認形參
function divideBy(a, b = 1) {
return a / b;
}
divideBy(10, 2); // 5
divideBy(5); // 5
- 注意
null或其他falsy值是被認為有值的
-
可選鏈操作符
?.(Optional chaining)
如果鏈條上的一個引用是nullish (null 或 undefined),.操作符會引起一個錯誤,?.操作符取而代之的是會按照短路計算的方式返回一個undefined。
if (this.props.data && this.props.data.list && this.props.data.list[0]) {
console.log(this.props.data.list[0])
// blabla...
}
// 使用 optional chain
if (this.props.data?.list?.[0]) {
console.log(this.props.data.list[0])
// blabla...
}
異步操作
setTimeout、異步請求等非同步代碼的callback,需要特別注意異步操作前后的狀態變化
-
要注意dom元素是否一直存在,組件是否unmount,特別是有對canvas的操作的時候
-
每個異步操作過程中的狀態,頁面上要有相應的loading
-
初始化的loading
-
按鈕點擊(防重復點擊)
-
網絡較差的時候容易暴露loading相關的問題
JSON
Uncaught SyntaxError: Unexpected token } in JSON at position 13
處理json的時候,一般都要使用try catch捕捉錯誤
const json = '{"a":1,"b":2}';
let result;
try {
result = JSON.parse(json);
} catch (e) {
// handle error
}
其他語法錯誤
Uncaught SyntaxError: Invalid or unexpected token
- 目前項目中均使用eslint,簡單的語法錯誤可以自動提示而避免
this.someMethod或者this.state.someState的拼寫錯誤,eslint無法查出來
外部攻擊的防御
XSS
Cross-Site Scripting(跨站腳本攻擊)簡稱 XSS,是一種代碼注入攻擊。攻擊者通過在目標網站上注入惡意腳本,使之在用戶的瀏覽器上運行。利用這些惡意腳本,攻擊者可獲取用戶的敏感信息如 Cookie、SessionID 等,進而危害數據安全。
CSRF
Cross-site request forgery(跨站請求偽造)簡稱CSRF 或者XSRF, 其原理是攻擊者構造網站后台某個功能接口的請求地址,誘導用戶去點擊或者用特殊方法讓該請求地址自動加載。攻擊者盜用了你的身份,以你的名義發送惡意請求。
對防御式編程采取防御的姿態
是不是防御式代碼越多越好呢? No
- 過度的防御式編程會使程序會變得臃腫而緩慢,增加軟件的復雜度。
要考慮好什么地方需要進行防御,然后因地制宜地調整進行防御式編程的優先級。
- 通用性防御措施 優於 細節性的防御
例如對於網絡請求,一般是統一處理超時、鑒權、各種錯誤code,而不是在業務層個別處理
- 根據使用場景,調整防御力度
如項目內部使用的utils函數和公開發布的package,后者防御要求更高
字節跳動數據平台前端團隊,在公司內負責大數據相關產品的研發。我們在前端技術上保持着非常強的熱情,除了數據產品相關的研發外,在數據可視化、海量數據處理優化、web excel、WebIDE、私有化部署、工程工具都方面都有很多的探索和積累,有興趣可以與我們聯系。
