搜尋
首頁web前端js教程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是否存在<pre class="brush:php;toolbar:false">let z = x &amp;&amp; x.y &amp;&amp; x.y.z复制代码</pre>經常這麼寫就顯得十分麻煩,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 
  • 前端不可避免地要跟各種各種瀏覽器、各種裝置打交道,一個非常重要的問題就是
  • 相容性
  • ,尤其是目前我們已經習慣了使用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 + ': "' + this.message + '"';
    }// 抛出自定义错误function f(){    try {        throw new UserException("Value too high");
        }catch(e){        if(e instanceof UserException){            console.log('catch UserException')            console.log(e)
            }else{            console.log('unknown error')            throw e
            }
        }finally{        // 可以做一些退出操作,如关闭文件、关闭loading等状态重置
            console.log('done')        return 1000 // 如果finally中return了值,那么会覆盖前面try或catch中的返回值或异常
        }
    }
    f()复制代码
    同步程式碼

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

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

    function a(){    throw 'error b'}// 当b能够处理异常时,则不再向上抛出function b(){    try{
            a()
        }catch(e){        if(e === 'error b'){            console.log('由b处理')
            }else {            throw e
            }
        }
    }function main(){    try {
            b()
        }catch(e){        console.log('顶层catch')
        }
    }复制代码
    非同步程式碼由於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中文網其他相關文章!

    陳述
    本文轉載於:juejin。如有侵權,請聯絡admin@php.cn刪除
    JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

    JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

    了解JavaScript引擎:實施詳細信息了解JavaScript引擎:實施詳細信息Apr 17, 2025 am 12:05 AM

    理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

    Python vs. JavaScript:學習曲線和易用性Python vs. JavaScript:學習曲線和易用性Apr 16, 2025 am 12:12 AM

    Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

    Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

    Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

    從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

    從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

    JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

    不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

    超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

    JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

    使用Next.js(後端集成)構建多租戶SaaS應用程序使用Next.js(後端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:23 AM

    我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

    See all articles

    熱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尊渡假赌尊渡假赌尊渡假赌

    熱工具

    MinGW - Minimalist GNU for Windows

    MinGW - Minimalist GNU for Windows

    這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

    SublimeText3 英文版

    SublimeText3 英文版

    推薦:為Win版本,支援程式碼提示!

    SublimeText3漢化版

    SublimeText3漢化版

    中文版,非常好用

    SAP NetWeaver Server Adapter for Eclipse

    SAP NetWeaver Server Adapter for Eclipse

    將Eclipse與SAP NetWeaver應用伺服器整合。

    PhpStorm Mac 版本

    PhpStorm Mac 版本

    最新(2018.2.1 )專業的PHP整合開發工具