TypeScript 中 for 迴圈的最佳實踐

TypeScript 中 for 迴圈的最佳實踐

本文將解釋在 TypeScript 中使用 for 迴圈的最佳實踐。

YouTube Video

TypeScript 中 for 迴圈的最佳實踐

for 語句是一種用於執行重複操作的基礎且強大的語法。在 TypeScript 中,利用類型安全性並以可讀性和可維護性為目標編寫代碼,可以讓您撰寫更高效且錯誤較少的代碼。

選擇正確的迴圈類型

TypeScript 提供了幾種不同的迴圈結構,適用於不同的使用案例。選擇適合的迴圈對於程式碼的清晰度和性能至關重要。

標準 for 迴圈

1for (let i = 0; i < 10; i++) {
2    console.log(i);
3}

當您需要精確控制迴圈索引時,標準的 for 迴圈是理想的選擇。

最佳實踐
1const maxIterations = 10;
2for (let i = 0; i < maxIterations; i++) {
3    console.log(i);
4}

在撰寫 for 語句時,記住以下要點可以幫助您撰寫出更安全且易於閱讀的代碼。

  • 使用 let 作為索引變數
    • 使用 let 替代 var 能將範圍限於區塊內,防止意外行為的發生。
  • 使用常量和具描述性的變數名稱,使循環邊界更明確
    • 避免使用魔術數字並使用有意義的變數名稱可以提高可讀性。

for...of 迴圈

1const array = [1, 2, 3];
2for (let value of array) {
3    console.log(value);
4}

for...of 迴圈適用於迭代如陣列和字串等可迭代對象。

最佳實踐
1const array = [1, 2, 3];
2for (const value of array) {
3    console.log(value);
4}

在撰寫 for...of 循環時,注意以下要點可以幫助您撰寫出更安全的代碼。

  • 對於迴圈變數使用 const
    • 如果循環內的值不被修改,使用 const 可以防止意外的重新賦值。

for...in 迴圈

1const obj = { a: 1, b: 2, c: 3 };
2for (const key in obj) {
3    console.log(`${key}: ${obj[key]}`);
4}

for...in 迴圈會迭代物件的可列舉屬性。

最佳實踐
1const obj = { a: 1, b: 2, c: 3 };
2for (const key in obj) {
3    if (obj.hasOwnProperty(key)) {
4        console.log(`${key}: ${obj[key]}`);
5    }
6}

在編寫 for...in 循環時,您可以考慮以下幾點。

  • 篩選屬性
    • 如果需要避免繼承的屬性,可以使用 hasOwnProperty
  • 不要對陣列使用 for...in 避免對陣列使用 for...in。它可能會迭代所有可枚舉屬性,包括那些不是陣列索引的屬性。

forEach 方法

1const array = [1, 2, 3];
2array.forEach((value, index) => {
3    console.log(`Index: ${index}, Value: ${value}`);
4});

在迭代陣列時,forEach 簡潔且免去了管理索引的需求。

最佳實踐

在使用 forEach 方法時,您可以考慮以下幾點。

  • 使用箭頭函數
    • 使用簡潔的箭頭函數以提高可讀性。
  • 避免中斷
    • forEach 不支援 breakcontinue。在需要時使用 for...offor 迴圈:。

型別安全與錯誤防範

透過運用 TypeScript 的型別系統,可避免迭代期間的執行時錯誤:。

為迴圈變數定義嚴格的型別

1const items = [1, 2, 3];
2items.forEach(item => {
3    console.log(item * 2);
4});
1const items: number[] = [1, 2, 3];
2items.forEach((item: number) => {
3    console.log(item * 2);
4});

通過明確指定循環變數的類型,可以及早檢測類型不匹配的情況。

避免隱式的 any 型別

1{
2  "compilerOptions": {
3    "noImplicitAny": true
4  }
5}

此外,通過在 tsconfig.json 中啟用 noImplicitAny,可以防止未顯式設定類型的變量被隱式分配為 any 類型。

在必要時使用 ReadonlyArray

1const numbers: ReadonlyArray<number> = [1, 2, 3];
2for (const value of numbers) {
3    console.log(value);
4}

如果您正在對不應修改的陣列進行迴圈操作,您可以使用 ReadonlyArray

效能考量

對於處理大型數據集或頻繁執行的迴圈而言,效能至關重要:。

選擇最佳的迴圈實現方式。

實現循環的方法有多種,它們在可讀性和執行效率方面各有不同。

 1// Prepare input data (an array from 1 to 1000000)
 2const input: number[] = Array.from({ length: 1000000 }, (_, i) => i + 1);
 3
 4// --- for ---
 5console.time('for loop');
 6const squaresFor: number[] = [];
 7for (let i = 0; i < input.length; i++) {
 8    squaresFor.push(input[i] * input[i]);
 9}
10console.timeEnd('for loop');
11
12// --- while ---
13console.time('while loop');
14const squaresWhile: number[] = [];
15let i: number = 0;
16while (i < input.length) {
17    squaresWhile.push(input[i] * input[i]);
18    i++;
19}
20console.timeEnd('while loop');
21
22// --- for-of ---
23console.time('for-of loop');
24const squaresForOf: number[] = [];
25for (const num of input) {
26    squaresForOf.push(num * num);
27}
28console.timeEnd('for-of loop');
29
30// --- forEach ---
31console.time('forEach loop');
32const squaresForEach: number[] = [];
33input.forEach((num: number): void => {
34    squaresForEach.push(num * num);
35});
36console.timeEnd('forEach loop');
37
38// --- map ---
39console.time('map');
40const squaresMap: number[] = input.map((value: number): number => value * value);
41console.timeEnd('map');

效率會根據執行環境的不同而有所差異,例如當循環執行一百萬次時,差異可能會變得相當明顯。在考慮可維護性和性能的基礎上,選擇最佳的迴圈方法。

使用本地迭代方法

 1const squares = [1, 2, 3].map(value => value * value);
 2console.log(squares);
 3
 4const numbers = [1, 2, 3, 4, 5, 6];
 5const evenNumbers = numbers.filter(value => value % 2 === 0);
 6console.log(evenNumbers); // [2, 4, 6]
 7
 8const squaredEvens = numbers
 9    .filter(value => value % 2 === 0) // Keep only even numbers
10    .map(value => value * value);     // Square the remaining values
11
12console.log(squaredEvens); // [4, 16, 36]

mapfilter 這樣的方法在某些情況下可能更具可讀性。

為了可讀性優先選擇 for...of

性能應僅在有限的情況下優先考慮;撰寫可讀性高的代碼通常更加重要。例如,優先使用 for...of 可以提高可讀性。

1const fruits = ["apple", "banana", "cherry"];
2
3for (let i = 0; i < fruits.length; i++) {
4    console.log(`${i}: ${fruits[i]}`);
5}
最佳實踐
1const fruits = ["apple", "banana", "cherry"];
2
3for (const [index, fruit] of fruits.entries()) {
4    console.log(`${index}: ${fruit}`);
5}

通過優先使用 for...of 循環,您可以撰寫出更具可讀性且抗錯能力更強的代碼。如本例所示,如果您還需要數組索引,將 entries()for...of 結合使用是有效的。

避免常見陷阱

在迭代期間修改集合

1const array = [1, 2, 3];
2for (const value of [...array]) {
3    if (value === 2) {
4        array.push(4); // Avoid this!
5    }
6}
7console.log(array);

在迭代期間修改陣列可能會導致意外行為:。必要時使用複本。

考慮邊緣情況

1const array: number[] = [];
2for (const value of array) {
3    console.log(value); // No output, but no errors
4}

此程式碼運行良好,但如果您需要處理空陣列,可以將其改進如下。

1const array: number[] = [];
2if (array.length === 0) {
3    console.log("The array is empty.");
4} else {
5    for (const value of array) {
6        console.log(value);
7    }
8}

考慮邊緣情況可以幫助防止後續程式碼中的錯誤。

結論

要掌握 TypeScript 中的 for 語句,了解各種迴圈結構、遵循型別安全的實踐以及優化效能至關重要。這些最佳實踐有助於您編寫更乾淨、更可靠且更易於維護的程式碼。

您可以在我們的 YouTube 頻道上使用 Visual Studio Code 來跟隨上述文章一起學習。 請也查看我們的 YouTube 頻道。

YouTube Video