目錄
空數組,但後台的實作卻是不回傳
在ES2020中提出了
合適的錯誤處理可以提要使用者體驗,在程式碼出錯時優雅地提示使用者
fulfilled
本地配置文件
Code Review
小结
首頁 web前端 js教程 javascript提高前端程式碼強大的一些方法,很好!

javascript提高前端程式碼強大的一些方法,很好!

Nov 19, 2020 pm 05:24 PM
javascript

javascript專欄介紹一些提高前端程式碼強大的方法。

javascript提高前端程式碼強大的一些方法,很好!

免費推薦:JavaScript(影片)


##在過去的開發經驗中處理了各種奇葩BUG,認識到程式碼健壯性(穩健性)是提高工作效率、生活品質的一個重要指標,本文主要整理了一些提高程式碼健壯性的思考。

之前整理過關於程式碼健全性相關的文章

正兒八經地寫JavaScript之單元測試如何在程式碼中打日誌

  • 本文將繼續探討除了單元測試、打日誌之外其餘一些幫助提升JavaScript程式碼健全性的方法。 更安全地存取物件不要相信介面資料
  • 不要相信前端傳的參數,也不要信任後台傳回的資料
例如某個

api/xxx/list的接口,按照文檔的約定

{
    code: 0,
    msg: "",
    data: [     // ... 具体数据
    ],
};复制代码
登入後複製
前端程式碼可能就會寫成
const {code, msg, data} = await fetchList()
data.forEach(()=>{})复制代码
登入後複製

因為我們假設了後台返回的data是一個數組,所以直接使用了

data.forEach
,如果在聯調的時候遺漏了一些異常情況

預期在沒有資料時data會回傳

[]

空數組,但後台的實作卻是不回傳

data

欄位後續介面更新,data從陣列變成了一個字典,跟前端同步不及時這些時候,使用data.forEach

時就會報錯,

Uncaught TypeError: data.forEach is not a function所以在這些直接使用後台介面傳回值的地方,最好加入型別偵測

Array.isArray(data) && data.forEach(()=>{})复制代码
登入後複製
同理,後台在處理前端請求參數時,也應進行相關的型別偵測。 空值合併運算子

由於JavaScript動態特性,我們在查詢物件某個屬性時如

x.y.z

,最好偵測一下

x

y是否存在<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">let z = x &amp;&amp; x.y &amp;&amp; x.y.z复制代码</pre><div class="contentsignin">登入後複製</div></div>經常這麼寫就顯得十分麻煩,dart中安全存取物件屬性就簡單得多

var z = a?.y?.z;复制代码
登入後複製

在ES2020中提出了

空值合併運算子

的草案,包括

??
  • ?.
  • 運算符,可以實現與dart相同的安全存取物件屬性的功能。目前打開最新版Chrome就可以進行測試了

在此之前,我們可以封裝一個安全取得物件屬性的方法
    function getObjectValueByKeyStr(obj, key, defaultVal = undefined) {    if (!key) return defaultVal;    let namespace = key.toString().split(".");    let value,
            i = 0,
            len = namespace.length;    for (; i < len; i++) {
            value = obj[namespace[i]];        if (value === undefined || value === null) return defaultVal;
            obj = value;
        }    return value;
    }var x = { y: { z: 100,},};var val = getObjectValueByKeyStr(x, "y.z");// var val = getObjectValueByKeyStr(x, "zz");console.log(val);复制代码
    登入後複製
  • 前端不可避免地要跟各種各種瀏覽器、各種裝置打交道,一個非常重要的問題就是
  • 相容性
  • ,尤其是目前我們已經習慣了使用ES2015的特性來開發程式碼,
  • polyfill可以幫助解決我們大部分問題。

    記得異常處理

    參考:

    JS錯誤處理MDNjs建構ui的統一異常處理方案,這個系列的文章寫得非常好

    異常處理是程式碼健全性的首要保障,關於異常處理有兩個面向

    合適的錯誤處理可以提要使用者體驗,在程式碼出錯時優雅地提示使用者

    將錯誤處理進行封裝,可以減少開發量,將錯誤處理與程式碼解耦錯誤物件可以透過throw語句拋出一個自訂錯誤物件

    // Create an object type UserExceptionfunction UserException (message){  // 包含message和name两个属性
      this.message=message;  this.name="UserException";
    }// 覆盖默认[object Object]的toStringUserException.prototype.toString = function (){  return this.name + &#39;: "&#39; + this.message + &#39;"&#39;;
    }// 抛出自定义错误function f(){    try {        throw new UserException("Value too high");
        }catch(e){        if(e instanceof UserException){            console.log(&#39;catch UserException&#39;)            console.log(e)
            }else{            console.log(&#39;unknown error&#39;)            throw e
            }
        }finally{        // 可以做一些退出操作,如关闭文件、关闭loading等状态重置
            console.log(&#39;done&#39;)        return 1000 // 如果finally中return了值,那么会覆盖前面try或catch中的返回值或异常
        }
    }
    f()复制代码
    登入後複製

    同步程式碼

    對於同步程式碼,可以使用

    透過責任鏈模式封裝錯誤,即當前函數如果可以處理錯誤,則在catch中進行處理:如果無法處理對應錯誤,則重新將catch拋到上一層

    function a(){    throw &#39;error b&#39;}// 当b能够处理异常时,则不再向上抛出function b(){    try{
            a()
        }catch(e){        if(e === &#39;error b&#39;){            console.log(&#39;由b处理&#39;)
            }else {            throw e
            }
        }
    }function main(){    try {
            b()
        }catch(e){        console.log(&#39;顶层catch&#39;)
        }
    }复制代码
    登入後複製
    非同步程式碼

    由於catch無法取得非同步程式碼中拋出的例外狀況,為了實現責任鏈,需要把異常處理透過回呼函數的方式傳遞給非同步任務

    function a(errorHandler) {    let error = new Error("error a");    if (errorHandler) {
            errorHandler(error);
        } else {        throw error;
        }
    }function b(errorHandler) {    let handler = e => {        if (e === "error b") {            console.log("由b处理");
            } else {
                errorHandler(e);
            }
        };    setTimeout(() => {
            a(handler);
        });
    }let globalHandler = e => {    console.log(e);
    };
    b(globalHandler);复制代码
    登入後複製
    Prmise的異常處理

    Promise只包含三種狀態:pendingrejected

    fulfilled

    let promise2 = promise1.then(onFulfilled, onRejected)复制代码
    登入後複製
    下面是promise拋出異常的幾條規則###
    function case1(){    // 如果promise1是rejected态的,但是onRejected返回了一个值(包括undifined),那么promise2还是fulfilled态的,这个过程相当于catch到异常,并将它处理掉,所以不需要向上抛出。
        var p1 = new Promise((resolve, reject)=>{        throw 'p1 error'
        })
    
        p1.then((res)=>{        return 1
        }, (e)=>{        console.log(e)        return 2
        }).then((a)=>{        // 如果注册了onReject,则不会影响后面Promise执行
            console.log(a) // 收到的是2
        })
    }function case2(){    //  在promise1的onRejected中处理了p1的异常,但是又抛出了一个新异常,,那么promise2的onRejected会抛出这个异常
        var p1 = new Promise((resolve, reject)=>{        throw 'p1 error'
        })
        p1.then((res)=>{        return 1
        }, (e)=>{        console.log(e)        throw 'error in p1 onReject'
        }).then((a)=>{}, (e)=>{        // 如果p1的 onReject 抛出了异常
            console.log(e)
        })
    }function case3(){    // 如果promise1是rejected态的,并且没有定义onRejected,则promise2也会是rejected态的。
        var p1 = new Promise((resolve, reject)=>{        throw 'p1 error'
        })
    
        p1.then((res)=>{        return 1
        }).then((a)=>{        console.log('not run:', a)
        }, (e)=>{        // 如果p1的 onReject 抛出了异常
            console.log('handle p2:', e)
        })
    }function case4(){    // // 如果promise1是fulfilled态但是onFulfilled和onRejected出现了异常,promise2也会是rejected态的,并且会获得promise1的被拒绝原因或异常。
        var p1 = new Promise((resolve, reject)=>{
            resolve(1)
        })
        p1.then((res)=>{        console.log(res)        throw 'p1 onFull error'
        }).then(()=>{}, (e)=>{        console.log('handle p2:', e)        return 123
        })
    }复制代码
    登入後複製
    ###因此,我們可以在###onRejected###中處理目前promise的錯誤,如果不能,,就把他拋給下一個###promise##########async#########async/await###本質上是promise的語法糖,因此也可以使用###promise.catch###類似的捕獲機制###
    function sleep(cb, cb2 =()=>{},ms = 100) {
        cb2()    return new Promise((resolve, reject) => {        setTimeout(() => {            try {
                    cb();
                    resolve();
                }catch(e){
                    reject(e)
                }
            }, ms);
        });
    }// 通过promise.catch来捕获async function case1() {    await sleep(() => {        throw "sleep reject error";
        }).catch(e => {        console.log(e);
        });
    }// 通过try...catch捕获async function case2() {    try {        await sleep(() => {            throw "sleep reject error";
            })
        } catch (e) {        console.log("catch:", e);
        }
    }// 如果是未被reject抛出的错误,则无法被捕获async function case3() {    try {        await sleep(()=>{}, () => {            // 抛出一个未被promise reject的错误
                throw 'no reject error'
            }).catch((e)=>{            console.log('cannot catch:', e)
            })
        } catch (e) {        console.log("catch:", e);
        }
    }复制代码
    登入後複製
    ###更穩定的第三方模組######在實現一些比較小功能的時候,例如日期格式化等,我們可能不習慣從npm找一個成熟的庫,而是自己順手寫一個功能包,由於開發時間或者測試用例不足,當遇見一些未考慮的邊界條件,就容易出現BUG。 ###

    这也是npm上往往会出现一些很小的模块,比如这个判断是否为奇数的包:isOdd,周下载量居然是60来万。

    使用一些比较成熟的库,一个很重要原因是,这些库往往经过了大量的测试用例和社区的考验,肯定比我们顺手些的工具代码更安全。

    一个亲身经历的例子是:根据UA判断用户当前访问设备,正常思路是通过正则进行匹配,当时为了省事就自己写了一个

    export function getOSType() {  const ua = navigator.userAgent  const isWindowsPhone = /(?:Windows Phone)/.test(ua)  const isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone  const isAndroid = /(?:Android)/.test(ua)  // 判断是否是平板
      const isTablet =    /(?:iPad|PlayBook)/.test(ua) ||
        (isAndroid && !/(?:Mobile)/.test(ua)) ||
        (/(?:Firefox)/.test(ua) && /(?:Tablet)/.test(ua))  // 是否是iphone
      const isIPhone = /(?:iPhone)/.test(ua) && !isTablet  // 是否是pc
      const isPc = !isIPhone && !isAndroid && !isSymbian && !isTablet  return {
        isIPhone,
        isAndroid,
        isSymbian,
        isTablet,
        isPc
      }
    }复制代码
    登入後複製

    上线后发现某些小米平板用户的逻辑判断出现异常,调日志看见UA为

    "Mozilla/5.0 (Linux; U; Android 8.1.0; zh-CN; MI PAD 4 Build/OPM1.171019.019) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 Quark/3.8.5.129 Mobile Safari/537.36复制代码
    登入後複製

    即使把MI PAD添加到正则判断中临时修复一下,万一后面又出现其他设备的特殊UA呢?所以,凭借自己经验写的很难把所有问题都考虑到,后面替换成mobile-detect这个库。

    使用模块的缺点在于

    • 可能会增加文件依赖体积,增加打包时间等,这个问题可以通过打包配置解决,将不会经常变更的第三方模块打包到vendor文件中配置缓存
    • 在某些项目可能会由于安全考虑需要减少第三方模块的使用,或者要求先进行源码code review

    当然在进行模块选择的时候也要进行各种考虑,包括稳定性、旧版本兼容、未解决issue等问题。当选择了一个比较好的工具模块之后,我们就可以将更多的精力放在业务逻辑中。

    本地配置文件

    在开发环境下,我们可能需要一些本地的开关配置文件,这些配置只在本地开发时存在,不进入代码库,也不会跟其他同事的配置起冲突。

    我推崇将mock模板托管到git仓库中,这样可以方便其他同事开发和调试接口,带来的一个问题时本地可能需要一个引入mock文件的开关

    下面是一个常见的做法:新建一个本地的配置文件config.local.js,然后导出相关配置信息

    // config.local.jsmodule.exports = {  needMock: true}复制代码
    登入後複製

    记得在.gitignore中忽略该文件

    config.local.js复制代码
    登入後複製

    然后通过try...catch...加载该模块,由于文件未进入代码库,在其他地方拉代码更新时会进入catch流程,本地开发则进入正常模块引入流程

    // mock/entry.jstry {  const { needMock } = require('./config.local')  if (needMock) {    require('./index') // 对应的mock入口
        console.log('====start mock api===')
      }
    } catch (e) {  console.log('未引入mock,如需要,请创建/mock/config.local并导出 {needMock: true}')
    }复制代码
    登入後複製

    最后在整个应用的入口文件判断开发环境并引入

    if (process.env.NODE_ENV === 'development') {  require('../mock/entry')
    }复制代码
    登入後複製

    通过这种方式,就可以在本地开发时愉快地进行各种配置,而不必担心忘记在提交代码前注释对应的配置修改~

    Code Review

    参考:

    • Code Review 是苦涩但有意思的修行

    Code Review应该是是上线前一个必经的步骤,我认为CR主要的作用有

    • 能够确认需求理解是否出现偏差,避免扯皮

    • 优化代码质量,包括冗余代码、变量命名和过分封装等,起码除了写代码的人之外还得保证审核的人能看懂相关逻辑

    对于一个需要长期维护迭代的项目而言,每一次commit和merge都是至关重要的,因此在合并代码之前,最好从头检查一遍改动的代码。即使是在比较小的团队或者找不到审核人员,也要把合并认真对待。

    小结

    本文主要整理了提高JavaScript代码健壮性的一些方法,主要整理了

    • 安全地访问对象属性,避免数据异常导致代码报错
    • 捕获异常,通过责任链的方式进行异常处理或上报
    • 使用更稳定更安全的第三方模块,
    • 认真对待每一次合并,上线前先检查代码

    此外,还需要要养成良好的编程习惯,尽可能考虑各种边界情况。

    以上是javascript提高前端程式碼強大的一些方法,很好!的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    本網站聲明
    本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

    熱AI工具

    Undresser.AI Undress

    Undresser.AI Undress

    人工智慧驅動的應用程序,用於創建逼真的裸體照片

    AI Clothes Remover

    AI Clothes Remover

    用於從照片中去除衣服的線上人工智慧工具。

    Undress AI Tool

    Undress AI Tool

    免費脫衣圖片

    Clothoff.io

    Clothoff.io

    AI脫衣器

    AI Hentai Generator

    AI Hentai Generator

    免費產生 AI 無盡。

    熱門文章

    R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
    1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O.最佳圖形設置
    1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O.如果您聽不到任何人,如何修復音頻
    1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O.聊天命令以及如何使用它們
    1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

    熱工具

    記事本++7.3.1

    記事本++7.3.1

    好用且免費的程式碼編輯器

    SublimeText3漢化版

    SublimeText3漢化版

    中文版,非常好用

    禪工作室 13.0.1

    禪工作室 13.0.1

    強大的PHP整合開發環境

    Dreamweaver CS6

    Dreamweaver CS6

    視覺化網頁開發工具

    SublimeText3 Mac版

    SublimeText3 Mac版

    神級程式碼編輯軟體(SublimeText3)

    如何使用WebSocket和JavaScript實現線上語音辨識系統 如何使用WebSocket和JavaScript實現線上語音辨識系統 Dec 17, 2023 pm 02:54 PM

    如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

    WebSocket與JavaScript:實現即時監控系統的關鍵技術 WebSocket與JavaScript:實現即時監控系統的關鍵技術 Dec 17, 2023 pm 05:30 PM

    WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

    如何利用JavaScript和WebSocket實現即時線上點餐系統 如何利用JavaScript和WebSocket實現即時線上點餐系統 Dec 17, 2023 pm 12:09 PM

    如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

    如何使用WebSocket和JavaScript實現線上預約系統 如何使用WebSocket和JavaScript實現線上預約系統 Dec 17, 2023 am 09:39 AM

    如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

    JavaScript與WebSocket:打造高效率的即時天氣預報系統 JavaScript與WebSocket:打造高效率的即時天氣預報系統 Dec 17, 2023 pm 05:13 PM

    JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

    簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

    JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

    javascript如何使用insertBefore javascript如何使用insertBefore Nov 24, 2023 am 11:56 AM

    用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

    JavaScript與WebSocket:打造高效率的即時影像處理系統 JavaScript與WebSocket:打造高效率的即時影像處理系統 Dec 17, 2023 am 08:41 AM

    JavaScript是一種廣泛應用於Web開發的程式語言,而WebSocket則是一種用於即時通訊的網路協定。結合二者的強大功能,我們可以打造一個高效率的即時影像處理系統。本文將介紹如何利用JavaScript和WebSocket來實作這個系統,並提供具體的程式碼範例。首先,我們需要明確指出即時影像處理系統的需求和目標。假設我們有一個攝影機設備,可以擷取即時的影像數

    See all articles