編寫可維護的 JavaScript

幾乎每個程序員都有接手維護別人遺留項目的經歷。愛掏網 - it200.com或者,有可能一個老項目某一天又被重新啟動。愛掏網 - it200.com 通常情況下,接手老項目都會讓人恨不得拋棄掉整個代碼庫從頭開始。愛掏網 - it200.com老代碼凌亂、文檔缺失、需要研究很多天才能完全搞明白它。愛掏網 - it200.com然而,通過合適的規劃、分解和 好的工作流,項目代碼可以變得干凈、有組織和可擴展。愛掏網 - it200.com

我曾經接手清理許多項目的代碼,讓我不得不重頭開始的項目真心不多,不過我最近就遇到了一個。愛掏網 - it200.com我從中學到了很多關于 JavaScript 代碼組織的內容,以及最重要的是冷靜,不要為你的前任抓狂。愛掏網 - it200.com在這篇文章里,我想要讓你知道我是怎么一步步處理項目代碼的,告訴你我的經驗。愛掏網 - it200.com

分析項目

第一步是先概覽整個項目,弄明白問題所在。愛掏網 - it200.com如果這是一個網站,通過點擊測試所有功能點:打開模塊、提交表單以及其他的。愛掏網 - it200.com在做這件事的時候,打開開發者工具,看看是否有任何報錯,看看控制臺有沒有日志。愛掏網 - it200.com如果這是一個Node.js項目,打開命令行界面然后檢查各個API。愛掏網 - it200.com最好的情況是項目通過統一的入口(例如:main.js,?index.js,?app.js, ……)來初始化所有的模塊,最差的情況是整個業務邏輯散落于各處。愛掏網 - it200.com

搞清楚項目采用了哪些工具。愛掏網 - it200.com是?jQuery、React?或是?Express?將所有重要的信息整理在一個清單里。愛掏網 - it200.com假設這個項目是用?Angular 2?寫的,你之前對 Angular 2 不熟悉,先去查看文檔,對它有一個初步的了解,并尋找最佳實踐的范例。愛掏網 - it200.com

從更高層面上理解項目

了解技術點是一個好開端,但要進一步深入理解,我們需要看一看它的單元測試。愛掏網 - it200.com單元測試通過測試代碼的功能函數與方法來保證代碼如預期的那樣運行。愛掏網 - it200.com不同于僅僅閱讀代碼,查看和運行單元測試能讓你更加深入理解代碼的期望運行結果。愛掏網 - it200.com如果接手的項目沒寫單元測試,沒關系,我們可以自己來寫。愛掏網 - it200.com

創建基線

這樣做是為了確立一致性。愛掏網 - it200.com你已經了解了項目的工具鏈、代碼結構、模塊的邏輯關系,現在該為項目創建基線了。愛掏網 - it200.com我推薦添加一個?.editorconfig?文件讓代碼風格在不同的編輯器、IED和不同的開發者之間保持一致。愛掏網 - it200.com

一致的縮進

使用 tab 還是空格來縮進是一個老問題?,常引發程序員爭論不休,不過沒關系,不管項目用的是空格還是 tab,繼續使用之前的就好了。愛掏網 - it200.com除非代碼庫既有空格縮進又有 tab 縮進的代碼,那就只好在兩者中做出一個取舍。愛掏網 - it200.com每個人都可以保持自己的觀點,但一個好的項目要保證所有的開發者都可以無爭議地協同工作。愛掏網 - it200.com

為什么這個很重要?因為每一個人都有自己使用編輯器或 IDE 的習慣。愛掏網 - it200.com比如,我是個代碼折疊控,要是編輯器沒有正確的代碼折疊功能,我整個人都會迷失在文件里。愛掏網 - it200.com如果代碼的縮進不一致,就會影響到折疊功能,因此每一次我打開一個文件,不得不先修復那些縮進然后才能開始工作,這十分浪費時間。愛掏網 - it200.com

// 雖然這段 JavaScript 代碼是合法的, 但這段代碼塊沒辦法正常折疊
// 因為這段代碼的縮進不一致
 function foo (data) {
  let property = String(data);

if (property === 'bar') {
   property = doSomething(property);
  }
  //... more logic.
 }

// 修復縮進后,這段代碼才可以被正確折疊
// 從而獲得更好的編碼體驗和更整潔的代碼庫
function foo (data) {
 let property = String(data);

 if (property === 'bar') {
  property = doSomething(property);
 }
 //... more logic.
}

命名

確保項目中使用命名約定是值得推崇的做法。愛掏網 - it200.com駝峰命名通常在 JavaScript 代碼中使用,但是我看到過許多不一致的命名。愛掏網 - it200.com例如:jQuery 項目經常會有代碼在命名上混淆 jQuery 對象和其他對象。愛掏網 - it200.com

// 不一致的命名讓代碼變得難以檢查和理解
// 它還會誤導維護者
const $element = $('.element');

function _privateMethod () {
  const self = $(this);
  const _internalElement = $('.internal-element');
  let $data = element.data('foo');
  //... more logic.
}

// 這樣改就更易于理解了
const $element = $('.element');

function _privateMethod () {
  const $this = $(this);
  const $internalElement = $('.internal-element');
  let elementData = $element.data('foo');
  //... more logic.
}

代碼檢查

之前所做的一切是在美化代碼,主要是讓它變得更易于檢查。愛掏網 - it200.com接下來我們介紹保證代碼質量的通用最佳實踐。愛掏網 - it200.comESLint,JSLint,還有?JSHint?是目前最受歡迎的三個 JavaScript 代碼檢查工具。愛掏網 - it200.com我個人用 JSHint 最多,但我現在最喜歡 ESLint,主要是因為它可以自定義規則,也較早地支持了 ES2024。愛掏網 - it200.com

一旦你開始代碼檢查,如果有很多錯誤信息出現,立即修復它們。愛掏網 - it200.com別跳過這些步驟,直到你的代碼檢查工具對你的代碼徹底滿意了。愛掏網 - it200.com

升級依賴

升級依賴需要仔細些,如果你不注意依賴本身的升級帶來的一些變化,就很容易導致錯誤。愛掏網 - it200.com一些項目可能只能依賴某些庫的固定版本(例如:v1.12.5),而另一些則使用版本通配符(例如:v1.12.x)。愛掏網 - it200.com如果你要快速升級依賴,你需要知道依賴模塊的版本號通常按如下規則建立:主版本.小版本.補丁。愛掏網 - it200.com如果你對?semantic versioning?的方式不熟悉,我推薦你先閱讀 Tim Oxley 的這篇文章。愛掏網 - it200.com

升級依賴沒有通用方法。愛掏網 - it200.com每個項目是不一樣的,需要區別對待。愛掏網 - it200.com升級依賴的補丁版本通常不會出什么問題,小版本一般也還OK。愛掏網 - it200.com但如果你要升級依賴的主版本,你就需要仔細檢查版本升級帶來的改變。愛掏網 - it200.com有可能 API 完全改變了,那樣你就得重寫你項目的一大堆代碼。愛掏網 - it200.com如果非必要,我一般避免將依賴升級到下一個主版本。愛掏網 - it200.com

如果你的項目使用?npm?來管理依賴,你可以很方便地使用?npm outdated?命令來檢查你的依賴是否已經過時了。愛掏網 - it200.com我用一個項目?FrontBook?來舉例說明,在這個項目里,我經常升級所有的依賴:

如你所見,我這個項目里的依賴有很多主版本升級。愛掏網 - it200.com我不會一次將他們全部升級,但是會一次升級一個。愛掏網 - it200.com雖然這會耗費許多時間,但這是確保不會出問題的唯一辦法(尤其是如果這個項目沒有任何測試)。愛掏網 - it200.com

下面該干臟活了

我必須讓你知道的非常重要的一點是,清理代碼并不意味著需要移除和重寫大量的代碼片段。愛掏網 - it200.com當然,有時候這可能是唯一的解決辦法,但是這不應該是你的首選方案。愛掏網 - it200.comJavaScript 特別靈活,因此難以給出一般性的建議,通常情況下你必須對癥下藥。愛掏網 - it200.com

建立單元測試

單元測試能保證你理解代碼是如何工作的,這樣避免一些意外導致錯誤。愛掏網 - it200.comJavaScript 單元測試的內容足夠寫另一篇文章,所以我在這里不能詳細介紹。愛掏網 - it200.com目前被廣泛使用的單元測試框架有?Karma、Jasmine、Mocha?以及?Ava。愛掏網 - it200.com如果你還要測試你的用戶界面,Nightwatch.js?和?DalekJS?是適合瀏覽器自動化測試的工具。愛掏網 - it200.com

單元測試和瀏覽器自動化測試的區別是,前者測試你的 JavaScript 代碼本身,來確保你所有的模塊和主要邏輯運行無誤。愛掏網 - it200.com后者,測試用戶界面,確保界面元素在正確的位置,且如預期地工作。愛掏網 - it200.com

在你開始動手重構代碼之前,認真對待單元測試,那樣你的項目的穩定性將得到改善,而你甚至還沒有開始考慮可擴展性。愛掏網 - it200.com單元測試帶來的另一個好處是你不再需要無時無刻擔心你的改動會無意中破壞原有功能。愛掏網 - it200.com

Rebecca Murphey 寫了一篇很棒的文章關于如何為現有代碼寫單元測試。愛掏網 - it200.com

架構

JavaScript 架構是另一個大話題。愛掏網 - it200.com重構和清理架構歸結于你在這方面積累了多少經驗。愛掏網 - it200.com我們可以選擇許多不同的設計模式,但不是所有的模式都適合于提升可擴展性。愛掏網 - it200.com限于篇幅,我不能涵蓋所有模式,但我至少可以給你一些通用的建議。愛掏網 - it200.com

首先,你需要找出哪些設計模式在你的項目中已經使用到了。愛掏網 - it200.com閱讀有關這些模式的部分,確保它們在項目中使用上保持一致性。愛掏網 - it200.com可擴展性的關鍵之一便是堅持一致的模式,避免混搭。愛掏網 - it200.com當然,你可以針對項目中的不同目的采用不同的設計模式(例如,將單例模式用于數據結構和短命名空間的輔助功能函數,以及將觀察者模式用于與模塊),但是別對一個模塊使用了一種設計模式,對另一個模塊又用另一種不同的設計模式。愛掏網 - it200.com

如果你的項目沒有任何架構(可能一切都堆在一個巨大的app.js文件里),從現在開始讓它有架構。愛掏網 - it200.com不過別指望一口吃成 胖子,需要一點一點來。愛掏網 - it200.com再次強調,沒有對任何項目都適用的萬精油方案,每一個項目的情況都是不同的。愛掏網 - it200.com根據規模和復雜度不同,項目文件目錄結構各有不同。愛掏網 - it200.com通 常,最基本的原則是,目錄結構應當將第三方庫、模塊、數據以及負責初始化模塊與邏輯的入口文件(比如:index.jsmain.js)分開來。愛掏網 - it200.com

簡而言之就是模塊化。愛掏網 - it200.com

將一切模塊化?

模塊化不是解決 JavaScript 擴展性問題的唯一選擇。愛掏網 - it200.com模塊化增加了一層 API,開發者不得不額外去熟悉它們。愛掏網 - it200.com這雖然增加了麻煩,但是值得去做的。愛掏網 - it200.com模塊化的基本原則是將所有功能拆分為小模塊。愛掏網 - it200.com這么做了以后,不僅讓你更容易解決 代碼里的問題,也讓項目組的其他成員更容易協同工作。愛掏網 - it200.com每個模塊只做一件事,它們不用關心外部邏輯,可以被復用在不同的地方。愛掏網 - it200.com

如何將一大堆功能拆分成許多邏輯關聯的小模塊?讓我們來做做看:

// 這個例子使用 Fetch API 來請求一個服務器的 API
// 讓我們假設它返回一個 JSON 文件,包含一些基本信息
// 然后我們創建一個新的元素,統計 json 所有屬性的
// content 字段中的字符數,然后將結果插入 DOM 的某個位置。愛掏網 - it200.com
fetch('https://api.somewebsite.io/post/61454e0126ebb8a2e85d', { method: 'GET' })
  .then(response => {
    if (response.status === 200) {
      return response.json();
    }
  })
  .then(json => {
    if (json) {
      Object.keys(json).forEach(key => {
        const item = json[key];
        const count = item.content.trim().replace(/\s+/gi, '').length;
        const el = `
          
            

Total characters: ${count}

`; const wrapper = document.querySelector('.info-element'); wrapper.innerHTML = el; }); } }) .catch(error => console.error(error));

上面的代碼不是模塊化的。愛掏網 - it200.com所有的功能都耦合在一起。愛掏網 - it200.com想象一下,如果這是更復雜的函數,由于出了一些錯誤你必須調試它們,可能 API 沒返回,可能某些原因 JSON 內部的值被改變或者別的什么問題。愛掏網 - it200.com調試這一大坨代碼如同噩夢般,不是嗎?

讓我們將代碼按不同的職責拆分開來:

function createWrapperElement (cssClass, content) {
  const className = cssClass || 'default';
  const wrapperElement = document.createElement('div');
  const textElement = document.createElement('p');
  const textNode = document.createTextNode(`Total characters: ${content}`);

  wrapperElement.classList.add(className);
  textElement.appendChild(textNode);
  wrapperElement.appendChild(textElement);

  return wrapperElement;
}

// 前一個例子 .forEach 中的匿名函數也被我們抽出來形成一個模塊
function appendCharacterCount (config) {
  const wordCount = countCharacters(config.content);
  const wrapperElement = createWrapperElement(config.className, wordCount);
  const infoElement = document.querySelector('.info-element');

  infoElement.appendChild(wrapperElement);
}

好了,我們現在有了三個新模塊,讓我們看看重構之后的 fetch 調用:

fetch('https://api.somewebsite.io/post/61454e0126ebb8a2e85d', { method: 'GET' })
  .then(response => {
    if (response.status === 200) {
      return response.json();
    }
  })
  .then(json => {
    if (json) {
      Object.keys(json).forEach(key => appendCharacterCount(json[key]))
    }
  })
  .catch(error => console.error(error));

當然我們還可以進一步將 .then( ) 中的邏輯也抽出來形成模塊,不過我想我已經充分表達了模塊化的含義。愛掏網 - it200.com

如果不想模塊化呢?

如我前面提到的,將你的代碼拆成小模塊會增加額外的一層 API。愛掏網 - it200.com如果你不想這么做,但是又想要讓代碼易于與其他開發者一起維護,不拆函數也沒問題,你依然可以將你的代碼分解成更簡單的部分并把重點放在可測試的代碼上。愛掏網 - it200.com

為你的代碼撰寫文檔

文檔化是一個老生常談的話題。愛掏網 - it200.com程序員社區的一部分人提倡將一切文檔化,而另一部分人認為好代碼即是文檔。愛掏網 - it200.com我奉行中庸之道,我覺得代碼的可讀和可擴展之間需要保持平衡。愛掏網 - it200.com你可以使用?JSDoc?來幫助你實現文檔化。愛掏網 - it200.com

JSDoc 是一個 JavaScript 的 API 文檔生成器。愛掏網 - it200.com常用的編輯器和 IDE 都有支持它的插件。愛掏網 - it200.com我們看一個例子:

function properties (name, obj = {}) {
  if (!name) return;
  const arr = [];

  Object.keys(obj).forEach(key => {
    if (arr.indexOf(obj[key][name]) 

這個函數有兩個參數,遍歷一個對象,返回一個數組。愛掏網 - it200.com這也許不是一個過于復雜的方法,但是對于沒寫過這段代碼的人來說,搞懂它還是有點費勁。愛掏網 - it200.com此外,這個方法具體的作用也不是很明確。愛掏網 - it200.com讓我們對它文檔化:

/**
 * 遍歷一個對象,將將所有屬性對象的 'name' push 到一個新數組中
 * 如果有重復,只 push 一次
 * @param  {String}  propertyName - 屬性的名字
 * @param  {Object}  obj          - 你想要遍歷的對象
 * @return {Array}
 */
function getArrayOfProperties (propertyName, obj = {}) {
  if (!propertyName) return;
  const properties = [];
  Object.keys(obj).forEach(child => {
    if (properties.indexOf(obj[child][propertyName]) 

我沒有改變任何代碼,只是改了一下函數名,添加了一段簡短的注釋,就已經讓這段代碼的可讀性變得好多了。愛掏網 - it200.com

有條理的代碼提交工作流

重構本身是一項艱巨的任務。愛掏網 - it200.com為了能隨時回滾你的修改(假如你破壞了一些原有功能,過了一會才意識到,你可能就需要回滾代碼到之前版本),你的每一部 分修改,比如重寫了一個方法、重命名了一個名字空間,都應該及時提交到 git (或者 svn)。愛掏網 - it200.com這么做也許會讓你覺得麻煩,但這么做有助于讓你的清理工作更有條理。愛掏網 - it200.com

為你的重構工作開一個新的分支,千萬別總是在主線 (master) 上改。愛掏網 - it200.com因為主線版本你有可能需要臨時做一些更新或者隨時發布一些 bug fixes 到線上環境,而你又不能將你沒有經過測試的和未完成的重構一同發布到線上,因此我建議你還是應該在不同的分支上工作。愛掏網 - it200.com

在 GitHub 上有一份有趣的指導,是關于如何使用他們的版本控制流程的。愛掏網 - it200.com

別失去理智

除了用技術解決問題之外,有一個很重要的點很少被人提及:別為你的前任抓狂。愛掏網 - it200.com我無意指責任何人,但是我知道一些人經歷過這種情況。愛掏網 - it200.com我花了很多年的時間去理解和克服心理上的不爽。愛掏網 - it200.com我曾經對前任開發者們留下的代碼、解決方案感到有些抓狂,他們做的一切在我眼里看來都造成混亂。愛掏網 - it200.com

結果,這些消極情緒沒帶給我任何好處。愛掏網 - it200.com消極情緒會導致你過度重構,浪費你的時間,而且可能破壞一些原有功能,而這一切又導致你越來越惱怒。愛掏網 - it200.com你可能會 花費額外的時間去重寫一個本來毫無問題的模塊,沒有人會因此感謝你,因為你在做無用功。愛掏網 - it200.com先分析狀況,然后做有價值的重構。愛掏網 - it200.com在任何時候,你隨時可以對一個模 塊做一些細節的改進。愛掏網 - it200.com

一段代碼為什么寫成這樣往往是有歷史原因的,也許前任程序員沒有足夠的時間將代碼寫得足夠好、或者不知道有更好的寫法,或者別的什么原因。愛掏網 - it200.com我們都是過來人。愛掏網 - it200.com

整理一下

讓我們從頭回顧一下所有的步驟,為你的下一個項目創建一個 checklist:

  1. 分析項目
    • 先忘掉自己的開發者身份,以一個用戶的身份來看清它的全貌。愛掏網 - it200.com
    • 瀏覽代碼庫,列出項目使用的工具。愛掏網 - it200.com
    • 閱讀項目相關工具的文檔和最佳實踐。愛掏網 - it200.com
    • 瀏覽單元測試,從更高層面上了解項目。愛掏網 - it200.com
  2. 創建基線
    • 引入?.editorconfig?以保證在所有的編輯器和 IDE 下保持代碼風格一致。愛掏網 - it200.com
    • 使縮進風格一致,至于是用 tab 還是空格,無所謂。愛掏網 - it200.com
    • 執行命名約定。愛掏網 - it200.com
    • 如果代碼檢查工具不存在, 添加一個,可以是?ESLint、?JSLint?或者?JSHint。愛掏網 - it200.com
    • 升級依賴,但是需要格外小心,弄清楚到底升級了什么。愛掏網 - it200.com
  3. 清理代碼
    • 建立單元測試與瀏覽器自動化測試,可以使用一些工具,例如?Karma、Jasmine、或者?Nightwatch.js。愛掏網 - it200.com
    • 確保架構和設計模式保持一致。愛掏網 - it200.com
    • 不要混用?設計模式,堅持使用已經存在的設計模式。愛掏網 - it200.com
    • 決定你是否需要將代碼庫拆分成模塊。愛掏網 - it200.com每一個模塊應當具有單一的目的,模塊不用關心自身之外的其他邏輯。愛掏網 - it200.com
    • 如果你不想拆分模塊,把重點放在可測試的代碼上,把它們分解成更簡單的代碼塊。愛掏網 - it200.com
    • 恰當地命名你的函數,為代碼適當撰寫文檔,保持可讀和可擴展的平衡。愛掏網 - it200.com
    • 使用?JSDoc?來生成文檔。愛掏網 - it200.com
    • 定期提交代碼,特別在有重要改變時。愛掏網 - it200.com這樣如果有什么改錯了,可以方便回滾。愛掏網 - it200.com
  4. 別失去理智
    • 別為你的前任開發者抓狂。愛掏網 - it200.com負面情緒只會導致過度重構而浪費時間。愛掏網 - it200.com
    • 一段代碼為什么寫成這樣總是有原因的。愛掏網 - it200.com要牢記我們都是過來人。愛掏網 - it200.com

我非常希望這篇文章能幫到你。愛掏網 - it200.com如果你正在為代碼重構做這些努力,或者你有一些我沒有提到過的好建議,我希望你可以告訴我。愛掏網 - it200.com

為了學習工作與休閑娛樂互不沖突,現新建圈【碼農茶水鋪】用于程序員生活,愛好,交友,求職招聘,吐槽等話題交流,希望各位大神工作之余到茶水鋪來喝茶聊天。愛掏網 - it200.com了解更多

聲明:所有內容來自互聯網搜索結果,不保證100%準確性,僅供參考。如若本站內容侵犯了原著者的合法權益,可聯系我們進行處理。
發表評論
更多 網友評論0 條評論)
暫無評論

返回頂部

主站蜘蛛池模板: 医生好大好硬好爽好紧| 日本护士XXXXHD少妇| 国产麻豆精品一区二区三区V视界| 免费视频爱爱太爽了| 东北女人毛多水多牲交视频| 蜜中蜜3在线观看视频| 日本精品久久久久中文字幕| 国产午夜亚洲精品不卡| 久久成人福利视频| 韩国在线免费视频| 日本无卡码一区二区三区| 国产乱码一二三区精品| 久久丫精品国产亚洲av| 老子影院午夜伦不卡手机| 我的3个美艳馊子白莹小说| 向日葵视频app免费下载| 一级毛片美国一级j毛片不卡| 一级女人18片毛片免费视频| 羞羞漫画在线成人漫画阅读免费| 我要看一级黄色毛片| 午夜三级限制福利电影在线看| 一本一道精品欧美中文字幕| 男攻在开会男受在桌子底下| 在线播放国产视频| 亚洲国产精品乱码在线观看97| 四虎成年永久免费网站| 日韩亚洲翔田千里在线| 国产91精品在线| 一本丁香综合久久久久不卡网站| 热久久综合这里只有精品电影 | 国产精品国产三级专区第1集| 亚洲人av高清无码| AV羞羞漫画在线观看| 欧美最猛黑人xxxx黑人猛交| 国产欧美精品区一区二区三区| 久久国产视频网站| 精品深夜av无码一区二区| 在线观看不卡视频| 亚洲av永久青草无码精品| 青娱乐在线视频免费观看| 性xxxx视频播放免费|