阿川私房教材:
學 JavaScript 前端,帶作品集去面試!

63 個專案實戰,寫出作品集,讓面試官眼前一亮!

立即開始免費試讀!

JavaScript 寫起來有很大彈性,但如何改善自己的 JavaScript 品質呢?以下是一份實用的參考指南。

使用 TypeScript

改善 JS 的第一個技巧就是,不要寫 JS。寫 TypeScript 吧。它是微軟寫的一個語言,算是 JS 的母集合(一般的 JS 可以直接在 TS 裡面跑)。TS 在 JS 之上添加了一個全面的型別系統。長期下來,整個前端生態系,幾乎都支援 TS 語言了。下面介紹一下 TS 的優點。

TypeScript 可以確保「型別安全」

型別安全代表編譯器驗證了所有型別是否以「合法」的方式被使用。換句話說,如果你創建一個接受「數字」的函數 foo

function foo(someNum: number): number {
  return someNum + 5;
}

該「foo」函數呼叫時要傳一個數字:

正確用法

console.log(foo(2)); // prints "7"

錯誤用法

console.log(foo("two")); // invalid TS code

除了需要花時間加型別以外,沒有其他缺點了。好處則是顯而易見。型別安全針對常見錯誤提供了額外預防,這對於像 JS 這樣鬆散的語言來說,是一件好事。

TypeScript 的型別系統,讓你能重構大型應用程式

重構大型 JS 應用程式是一場噩夢。主要是因為它不會檢查函數簽名。也就是說,JS函數隨便呼叫,編譯器都不會先報錯。舉個例,如果我有一個函數「myAPI」,由 1000 個不同的服務使用:

function myAPI(someNum, someString) {
  if (someNum > 0) {
    leakCredentials();
  } else {
    console.log(someString);
  }
}

我稍微更改了呼叫簽名:

function myAPI(someString, someNum) {
  if (someNum > 0) {
    leakCredentials();
  } else {
    console.log(someString);
  }
}

我必須自已 100% 去確認,使用此功能的1000 個地方,都有正確更新用法。漏掉的地方,在執行時就會壞掉。在 TS 中相同的情境如下:

before

function myAPITS(someNum: number, someString: string) { ... }

after

function myAPITS(someString: string, someNum: number) { ... }

如您所見,「myAPITS」函數跟在 JavaScript 中一樣修改。但是,這段程式碼無法順利產出 JavaScript,而是會出現無效 TypeScript 的錯誤提示,因為用到它的1000個地方,現在型別就出錯了。正是因為「型別安全」,這 1000 個案例會阻止編譯。

TypeScript 使團隊溝通架構更容易

正確設定 TS 後,要先定義介面跟類型,不然很難寫程式碼。這也順便提供了一種簡潔、可溝通的架構提案方法。例如,如果我想跟後端提出新的「請求」類型,可以使用 TS 將以下內容提交給團隊。

interface BasicRequest {
  body: Buffer;
  headers: { [header: string]: string | string[] | undefined; };
  secret: Shhh;
}

雖然也是要寫一點程式碼,但可以先提供以上內容作為初步想法、取得回饋,而不用一次設計完功能。像這樣強迫開發者先定義介面跟 API,能讓程式碼品質更好。

總體而言,TS 已經發展成為一種成熟且更可預測的 vanilla JS 替代品。當然還是要熟 JS,但我現在大多數新專案都是直接寫 TS。

使用現代功能

JavaScript是世界上最流行的程式設計語言之一。你可能會以為這語言已經被百萬開發者摸透了,其實不是。很多新功能是近期才加上去的。

asyncawait

長期以來,非同步、事件驅動的 callback 是 JS 開發中不可避免的:

傳統 callback

makeHttpRequest('google.com', function (err, result) {
  if (err) {
    console.log('Oh boy, an error');
  } else {
    console.log(result);
  }
});

上述程式碼會有越寫越多層的問題。JS 添加了一個新概念叫 Promises,可以避免嵌套問題。

Promises

makeHttpRequest('google.com').then(function (result) {
  console.log(result);
}).catch(function (err) {
  console.log('Oh boy, an error');
});

與 callback 相比,Promise 的最大優勢是可讀性和可鏈性。

雖然 Promise 很棒,但它們仍然有一些不足之處。寫起來感覺有點怪。為了解決這個問題,ECMAScript 委員會添加一種使用 promise 的新方法,asyncawait

asyncawait

try {
  const result = await makeHttpRequest('google.com');
  console.log(result);
} catch (err) {
  console.log('Oh boy, an error');
}

需要注意的是,await 的內容必須先被宣告為 async

以前面 makeHttpRequest 舉例

async function makeHttpRequest(url) {
  // ...
}

也可以直接 await 一個 Promise,因為 async 函數實際上只是 Promise 比較花哨的包裝寫法。這也意味著,async/await 代碼和 Promise 代碼在功能上是等價的。因此,請大方使用 async/await 吧。

let and const

以前大家寫 JS 都只能用 varvar 的變數作用域有一些獨特規則,一直讓人很困惑。從 ES6 開始,有了 constlet 可以替代使用,幾乎不用再寫 var 了。

至於何時要用 const 何時要用 let 呢?永遠從 const 先開始使用。const 因為不可修改、更可預期,會讓程式碼品質更好。使用 let 的場景比較少,我寫 const 的頻率比 let 高 20 倍左右。

箭頭 => 函數

箭頭函數是在JS中宣告匿名函數的簡潔方法。通常作為 callback 或 event hook 傳遞。

傳統的匿名 function

someMethod(1, function () { // has no name
  console.log('called');
});

在大多數情況下,這種風格沒有任何“不對”。但它的變數作用域很“有趣”,常導致許多意想不到的錯誤。有了箭頭函數之後,就沒這問題了。

匿名箭頭函數

someMethod(1, () => { // has no name
  console.log('called');
});

除了更簡潔之外,箭頭函數還具有更實用的變數範圍界定。箭頭函數從定義它們的作用域繼承「this」。

在某些情況下,箭頭函數甚至可以更簡潔:

const added = [0, 1, 2, 3, 4].map((item) => item + 1);
console.log(added) // prints "[1, 2, 3, 4, 5]"

箭頭函數可以包括隱式的「return」語句。就不用寫大括弧、分號。

不過,這跟“var”被完全取代不同。傳統匿名函數仍然有需要的地方,例如類別方法定義。話雖如此,如果你總是預設使用箭頭函數,程式碼錯誤會少很多。

展開運算子 ...

提取一個物件的鍵/值對,並將它們添加為另一個物件的子物件,是一種很常見的場景。從歷史上看,有幾種方法可以實現,但通通都很笨拙:

const obj1 = { dog: 'woof' };
const obj2 = { cat: 'meow' };
const merged = Object.assign({}, obj1, obj2);
console.log(merged) // prints { dog: 'woof', cat: 'meow' }

這寫法以前很常見,也很囉唆。多虧了「展開運算子」,再也不需要那樣寫了:

const obj1 = { dog: 'woof' };
const obj2 = { cat: 'meow' };
console.log({ ...obj1, ...obj2 }); // prints { dog: 'woof', cat: 'meow' }

最棒的是,與陣列也可無縫接軌:

const arr1 = [1, 2];
const arr2 = [3, 4];
console.log([ ...arr1, ...arr2 ]); // prints [1, 2, 3, 4]

它可能不是近期 JS 中最重要的功能,但它是我的最愛之一。

範本文字(範本字串)

字串處理太常見了,程式語言要能處理範本字串,才夠好用。處理動態內容、多行文字時,都會需要它:

const name = 'Ryland';
const helloString =
`Hello
 ${name}`;

我認為程式碼不言自明。看起來好多了。

物件解構

物件解構是一種從資料集合(物件、陣列等)中提取值,而無需反覆運算數據或顯式訪問其鍵的方法:

老方法

function animalParty(dogSound, catSound) {}

const myDict = {
  dog: 'woof',
  cat: 'meow',
};

animalParty(myDict.dog, myDict.cat);

新方法

function animalParty(dogSound, catSound) {}

const myDict = {
  dog: 'woof',
  cat: 'meow',
};

const { dog, cat } = myDict;
animalParty(dog, cat);

不只這樣。您還可以在函數的簽名中定義解構:

解構範例二

function animalParty({ dog, cat }) {}

const myDict = {
  dog: 'woof',
  cat: 'meow',
};

animalParty(myDict);

它也適用於陣列:

解構範例三

[a, b] = [10, 20];

console.log(a); // prints 10

以上,希望對您有幫助!


共有 1 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。

阿川私房教材:
學 JavaScript 前端,帶作品集去面試!

63 個專案實戰,寫出作品集,讓面試官眼前一亮!

立即開始免費試讀!