1. 【JS】1150- 如何寫出更優(yōu)雅的 JavaScript 代碼

        共 4931字,需瀏覽 10分鐘

         ·

        2021-11-27 12:46

        有人說好的代碼像一首詩,優(yōu)雅而有內(nèi)涵。

        在日常開發(fā)中,維護別人老代碼的時候是不是總感覺邏輯混亂,無法入手,就跟屎山一樣?改一個 BUG 就如同在這座屎山上面艱難地再拉一坨……今天,我們從日常開發(fā)的角度上面談?wù)勅绾巫屪约旱拇a更清晰,更易于維護,讓別人看起來更有逼格。

        變量命名

        要寫出好代碼,變量命名至關(guān)重要。我們盡量采用富有表現(xiàn)力的詞,英文不好多用翻譯軟件,保證不出現(xiàn)錯誤單詞。編輯器可以安裝相關(guān)的拼寫檢查、翻譯插件。

        1. 不要縮寫/簡寫單詞,除非這些單詞已經(jīng)公認可以被這樣縮寫/簡寫。這樣做導(dǎo)致可讀性下降,意義表達不明確。反例:Association ass 、StringBuilder sb
        2. 普通變量命名則使用名詞及名詞短語。比如 value、options、fileText、columnName
        3. boolean 命名,如果表示“是不是”用 is...,表示“有沒有”用 has...,表示“能不能”用 can...,表示“能不能怎么樣”用 ...able
        4. function 命名采用動詞/賓語順序。比如 getUserInfo、insertRowsclearValue
        5. 避免使用 _ 開頭、temp、my 之類命名臨時變量,臨時變量也是有意義的,這些都會增加閱讀代碼時的噪點
        6. 避免無意義的命名,你起的每一個名字都要能表明意思。比如 userInfoclickCount 反例 info 、count

        代碼結(jié)構(gòu)

        好的代碼結(jié)構(gòu)有助于我們保持理性的思路,降低心智負擔(dān)。

        使用 const 定義

        如果沒有復(fù)雜的邏輯,用 const 就足夠了,這樣不用擔(dān)心變量被重新賦值而引起意外情況。當這種模式寫多之后,你會發(fā)現(xiàn)在項目中幾乎找不到幾個用 let 的地方。

        //?Bad
        let?result?=?false;
        if?(userInfo.age?>?30)?{
        ??result?=?true;
        }

        //?Good
        const?result?=?userInfo.age?>?30;

        邏輯歸類

        在復(fù)雜的邏輯中,相關(guān)的邏輯盡量放在一起,并插入空行分開。使代碼結(jié)構(gòu)看起來清晰明了。

        提前返回

        function 中經(jīng)常會遇到變量值為 undefined 的情況,這個時候則需要提前判斷并阻止執(zhí)行,避免一些不必要的分支(無 else),讓代碼更精煉。

        if?(!userInfo)?{
        ??return;
        }

        if?(!hasMoney)?{
        ??return;
        }

        //?執(zhí)行業(yè)務(wù)邏輯

        優(yōu)雅的條件判斷

        簡單的判斷 if + return 就提前返回了。復(fù)雜邏輯 if else if 面條式代碼不夠優(yōu)雅,想用 switch case?實際情況看來 if elseswitch case 用法區(qū)別不大。

        //?if?else
        if?(status?==?1)?{
        ??console.log('processing');
        }?else?if?(status?==?2)?{
        ??console.log('fail');
        }?else?if?(status?==?3)?{
        ??console.log('success');
        }?else?if?(status?==?4)?{
        ??console.log('cancel');
        }?else?{
        ??console.log('other');
        }

        //?switch?case
        switch?(status)?{
        ??case?1:
        ????console.log('processing');
        ????break;
        ??case?2:
        ????console.log('fail');
        ????break;
        ??case?3:
        ????console.log('success');
        ????break;
        ??case?4:
        ????console.log('cancel');
        ????break;
        ??default:
        ????console.log('other');
        ????break;
        }

        在上面代碼中可以看出 switch caseif else 代碼行數(shù)還多,break 關(guān)鍵字也是必不可少,還不忘寫 default。這里我們推薦用 ObjectMap 作為條件存儲。

        const?actions?=?{
        ??1:?'processing',
        ??2:?'fail',
        ??3:?'success',
        ??4:?'cancel',
        ??default:?'other',
        };

        console.log(actions[status]????actions.default);

        Map 則更為強大,對象的鍵只能是一個字符串或符號,但 Map 的鍵可以是對象或更多,可以作為條件聯(lián)合判斷。

        const?actions?=?new?Map([
        ??[/^sign_[1-3]$/,?()?=>?'A'],
        ??[/^sign_5$/,?()?=>?'B'],
        ??//...
        ]);

        const?action?=?[...actions].filter(([key,?value])?=>?key.test(`sign_${status}`));
        action.forEach(([key,?value])?=>?value());

        善用表達式

        善用表達式,避免面條式代碼。簡單的條件判斷可以用三元運算符代替。普通的 for 循環(huán)可以用 map、 forEach 代替。

        降低復(fù)雜度

        一個邏輯的代碼行數(shù)越多,維護起來越困難,這個時候我們就需要將相關(guān)的邏輯抽離到另一個 function 中,從而降低上下文的復(fù)雜度。這里我們建議是一個 function 的代碼量在 120 個字符的寬度下不超過一個屏幕。

        值得注意的是, function 所定義的形參最好控制在 3 個以內(nèi),否則容易疏忽傳入的順序,從而變得不易維護。如果參數(shù)太多就需要將相關(guān)的參數(shù)聚合成對象傳遞。

        移除重復(fù)代碼

        重復(fù)代碼在 Bad Smell 中排在第一位,所以,竭盡你的全力去避免重復(fù)代碼。因為它意味著當你需要修改一些邏輯時會有多個地方需要修改。

        引入順序

        import 中,我們約定將 node_modules 中的包放在前面,然后是相對路徑的包。有時一個 cssimport 順序不同就會導(dǎo)致執(zhí)行的優(yōu)先級不同。

        使用聲明式

        聲明式編程:告訴“機器”你想要的是什么(what),讓機器想出如何去做(how)。命令式編程:命令“機器”如何去做事情(how),這樣不管你想要的是什么(what),它都會按照你的命令實現(xiàn)。世界很美妙,遠離命令式,節(jié)省時間體驗生活。

        //?聲明式:篩選我需要的結(jié)果
        const?result?=?dataSource.filter((dataItem)?=>?dataItem.age?>?10);

        //?命令式:親力而為查找/追加數(shù)據(jù)
        let?result?=?[];
        dataSource.forEach((dataItem)?=>?{
        ??if?(dataItem.age?>?10)?{
        ????result.push(dataItem);
        ??}
        });

        這個時候有人就會說命令式編程性能好。其實我們寫代碼無需做過早優(yōu)化,那點性能損耗與可維護性比起來可以算是九牛一毛。

        寫好業(yè)務(wù)注釋

        優(yōu)秀的代碼命名無需注釋,代碼即注釋,加上注釋就會冗余。這時某個業(yè)務(wù)的邏輯就離不開準確的注釋,這樣可以幫助我們更加理解業(yè)務(wù)的詳細邏輯。需要要求的是代碼改動注釋也要隨之更新。

        函數(shù)式編程

        函數(shù)式編程獲得越來越多的關(guān)注,包括 react 都遵循這個理念。

        函數(shù)是"第一等公民"

        變量可以一個函數(shù),可以作為另一個函數(shù)的參數(shù)

        function?increaseOperator(user)?{
        ??return?user.age?+?1;
        }

        userList.filter(Boolean).map(increaseOperator);

        純函數(shù)

        即相同輸入,永遠會得到相同輸出,而且沒有任何可觀察的副作用。如果使用了 setTimeout 、Promise 或更多具有意外情況發(fā)生的操作。那么這類操作被稱之為 "副作用" Effect。

        每一個函數(shù)都可以被看做獨立單元。純函數(shù)的好處:方便組合、可緩存、可測試、引用透明、易于并發(fā)等等。

        //?不純的,?minimum?可能被其他操作改變
        let?minimum?=?21;

        function?checkAge(age)?{
        ??return?age?>=?minimum;
        }

        //?純的
        function?checkAge(age)?{
        ??const?minimum?=?21;
        ??return?age?>=?minimum;
        }

        比如 slicespliceslice 符合純函數(shù)的定義是因為對相同輸入它保證能返回相同輸出。而 splice 卻會改變調(diào)用它的數(shù)組,這就會產(chǎn)生可觀察到的副作用,即這個原始數(shù)組永久地改變了。

        var?countList?=?[1,?2,?3,?4,?5];

        //?純的
        countList.slice(0,?3);
        //=>?[1,?2,?3]

        countList.slice(0,?3);
        //=>?[1,?2,?3]

        //?不純的
        countList.splice(0,?3);
        //=>?[1,?2,?3]

        countList.splice(0,?3);
        //=>?[4,?5]

        不可變數(shù)據(jù)

        每次操作不修改原先的值,而是返回一個新的值,這與無副作用相呼應(yīng)。不可變數(shù)據(jù)模型易于調(diào)試,不用擔(dān)心當前數(shù)據(jù)被別的地方更改。

        //?Bad?修改了參數(shù)
        function?updateUser(user)?{
        ??user.age?=?10;
        }

        //?Good?返回新的對象
        function?updateUser(user)?{
        ??return?{
        ????...user,
        ????age:?10,
        ??};
        }

        這里推薦 immer 作為函數(shù)式不可變數(shù)據(jù)操作。

        完善的 TS 類型

        typescript 擁有強大的類型系統(tǒng),彌補了 javascript 在類型上的短板。我們在寫 typescript 代碼的時候需要注意的是,不使用隱式/顯示的 any 類型,若有不確定類型的情況,首先考慮的是 泛型 去約束它,其次則用 unknown 加斷言,最后才是 any。

        //?典型類型完備的函數(shù)
        function?pick<T,?K?extends?keyof?T>(obj:?T,?keys:?K[])?{
        ??return?Object.fromEntries(keys.map((key)?=>?[key,?obj[key]]))?as?Pick;
        }

        pick(userInfo,?['email',?'name']);

        結(jié)合 prettier + eslint

        在團隊協(xié)作中,統(tǒng)一的代碼尤為重要,目前社區(qū)中的 eslint 規(guī)則層出不窮。其中 eslint-config-airbnb 限制最為嚴格,很多開源團隊都在用。這里我們可以與 prettier 搭配用,并禁用其中互相沖突的規(guī)則。編碼指南 https://github.com/airbnb/javascript

        結(jié)語

        想寫出優(yōu)雅的代碼其實很簡單,千里之行,始于足下。我們需要不斷優(yōu)化自己的代碼,不要畏懼改善代碼質(zhì)量所需付出的努力。理解這些準則并實踐,假以時日,相信我們每個人寫代碼都能做到像寫詩一樣行云流水。

        1. JavaScript 重溫系列(22篇全)
        2. ECMAScript 重溫系列(10篇全)
        3. JavaScript設(shè)計模式 重溫系列(9篇全)
        4.?正則 / 框架 / 算法等 重溫系列(16篇全)
        5.?Webpack4 入門(上)||?Webpack4 入門(下)
        6.?MobX 入門(上)?||??MobX 入門(下)
        7. 120+篇原創(chuàng)系列匯總

        回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~

        點擊“閱讀原文”查看 130+ 篇原創(chuàng)文章

        瀏覽 28
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 红桃视频一区二小区三区四区 | 伦精品午夜一级婬片A片 | 99国产精品久久久久久久成人热 | 日本成人三级片网站 | 中文字幕乱码人妻二区三区 |