首頁 >web前端 >js教程 >現代 JavaScript 最佳實務 - 第 2 部分

現代 JavaScript 最佳實務 - 第 2 部分

Patricia Arquette
Patricia Arquette原創
2024-12-08 16:09:12681瀏覽

Mejores Prácticas en JavaScript Moderno - Parte 2

在本文的第一部分中,我們探討了現代 JavaScript 的基礎知識以及一些基本的最佳實踐,以開始編寫更清晰、更有效率的程式碼。但作為開發者,我們知道總是有更多東西需要學習和改進。

8. 可選連結 (?.)

在處理物件或巢狀結構時,我們有時需要在嘗試存取屬性之前檢查它是否存在。 可選連結運算子 (?.) 是一個強大的工具,可以簡化此任務,避免 null 或未定義值的屬性存取錯誤。

為什麼它有用?

想像一下您有一個複雜的物件結構,並且您不確定其中是否存在某些屬性。如果沒有可選鏈接,您將必須在每個步驟進行手動檢查,這可能會使您的程式碼更長且可讀性較差。使用 ?. 運算符,您可以安全地存取屬性並在任何中間屬性不存在時取得未定義的值。

基本範例

const producto = {};
const impuesto = producto?.precio?.impuesto;
console.log(impuesto); // undefined

在這種情況下,由於產品沒有價格屬性,因此可選鏈會傳回未定義而不是產生錯誤。

具有更複雜物件的範例

假設您有一個具有不同屬性的產品列表,其中一些可能為空或未定義:

const productos = [
  { nombre: 'Laptop', detalles: { precio: 1000 } },
  { nombre: 'Teléfono', detalles: null },
  { nombre: 'Tablet', detalles: { precio: 500, impuesto: 50 } }
];

// Acceso seguro a la propiedad 'impuesto' de cada producto
productos.forEach(producto => {
  const impuesto = producto?.detalles?.impuesto;
  console.log(impuesto); // undefined, null o el valor real
});

在此範例中,可選鏈允許我們在嘗試存取product.details.tax時避免錯誤,即使詳細資訊為空或不存在。

它如何改進您的程式碼?

  • 避免對空或未定義屬性的存取錯誤。
  • 簡化程式碼,讓安全檢查更清晰、更具可讀性。
  • 允許您處理不確定或不完整的資料,這在處理來自 API 或資料庫的回應時非常常見。

獎勵:可選的函數鏈接

可選鏈也可以與函數一起使用,當您的函數可能未在物件上定義時,這非常有用:

const usuario = { nombre: 'Juan', obtenerEdad: null };
const edad = usuario.obtenerEdad?.();
console.log(edad); // undefined

這裡,getAge函數是未定義的(它為空),但它不會拋出錯誤,它只是返回未定義。

9. 使用 async/await 進行非同步處理

當您在 JavaScript 中使用非同步操作時,例如從 API 取得資料或讀取文件,async/await 語法可能是您最好的朋友。 async/await 不是使用 .then() 和 .catch() 中的 Promise,而是允許您以更清晰、更易讀的方式編寫非同步程式碼,類似於我們編寫同步程式碼的方式。

為什麼要使用非同步/等待?

  • 簡單性和可讀性:您可以避免閱讀和維護變得複雜的「承諾鏈」。
  • 更直覺的錯誤處理:使用try/catch處理錯誤比使用.catch()更清楚。
  • 更精確的控制:允許在函數中的任何地方使用await,從而更容易控制更複雜的非同步流程。

基本使用範例:

假設我們正在使用一個傳回資料的 API。使用 async/await 而不是 .then() 使流程更容易遵循:

const producto = {};
const impuesto = producto?.precio?.impuesto;
console.log(impuesto); // undefined

實際範例:從 API 取得資料並將其顯示在 UI 中

假設您有一個網頁,您需要在其中顯示來自 API 的使用者資訊。以下是如何使用 async/await 取得資料並將其呈現到介面的範例:

const productos = [
  { nombre: 'Laptop', detalles: { precio: 1000 } },
  { nombre: 'Teléfono', detalles: null },
  { nombre: 'Tablet', detalles: { precio: 500, impuesto: 50 } }
];

// Acceso seguro a la propiedad 'impuesto' de cada producto
productos.forEach(producto => {
  const impuesto = producto?.detalles?.impuesto;
  console.log(impuesto); // undefined, null o el valor real
});

對象.values()

傳回一個包含物件屬性的所有值的陣列。當您只需要值而不需要鍵時,這是完美的。

範例:

const usuario = { nombre: 'Juan', obtenerEdad: null };
const edad = usuario.obtenerEdad?.();
console.log(edad); // undefined

對象.entries()

這是最通用的方法。傳回一個數組的數組,其中每個子數組包含一個鍵及其對應的值。如果您想在一次操作中同時使用鍵和值,這非常有用。

範例:

async function obtenerDatos() {
  try {
    const respuesta = await fetch('https://api.ejemplo.com/datos');
    if (!respuesta.ok) {
      throw new Error('Error al obtener los datos');
    }
    const datos = await respuesta.json();
    console.log(datos);
  } catch (error) {
    console.error('Error:', error.message);
  }
}

獎勵:使用 for...of 迭代

您是否知道可以將這些方法與 for...of 結合起來使您的程式碼更加簡潔?這是使用 Object.entries() 的範例:

範例:

// Función para obtener y mostrar los datos de usuarios
async function obtenerUsuarios() {
  try {
    const respuesta = await fetch('https://api.ejemplo.com/usuarios');
    if (!respuesta.ok) {
      throw new Error('No se pudieron cargar los usuarios');
    }
    const usuarios = await respuesta.json();
    mostrarUsuariosEnUI(usuarios);
  } catch (error) {
    console.error('Hubo un problema con la carga de los usuarios:', error);
    alert('Error al cargar los usuarios. Intenta más tarde.');
  }
}

// Función para renderizar usuarios en el HTML
function mostrarUsuariosEnUI(usuarios) {
  const contenedor = document.getElementById('contenedor-usuarios');
  contenedor.innerHTML = usuarios.map(usuario => `
    <div>



<h3>
  
  
  ¿Qué mejoramos con async/await?
</h3>

<ol>
<li>
<strong>Manejo claro de errores:</strong> Usamos try/catch para capturar cualquier error que pueda ocurrir durante la obtención de datos, ya sea un problema con la red o con la API.</li>
<li>
<strong>Código más legible:</strong> La estructura de await hace que el flujo del código se lea de manera secuencial, como si fuera código sincrónico.</li>
<li>
<strong>Evita el anidamiento:</strong> Con async/await puedes evitar los callbacks anidados (el famoso "callback hell") y las promesas encadenadas.</li>
</ol>

<p>Usar async/await no solo mejora la calidad de tu código, sino que también hace que sea mucho más fácil depurar y mantener proyectos a largo plazo. ¡Es una herramienta poderosa que deberías incorporar siempre que trabajes con asincronía en JavaScript!</p>

<h2>
  
  
  10. Métodos modernos para objetos
</h2>

<p>Cuando trabajamos con objetos en JavaScript, es común que necesitemos iterar sobre las claves y los valores, o incluso extraer solo las claves o valores. Los métodos modernos como Object.entries(), Object.values() y Object.keys() hacen que estas tareas sean mucho más fáciles y legibles.</p>

<h3>
  
  
  Object.keys()
</h3>

<p>Este método devuelve un array con todas las claves de un objeto. Es útil cuando solo necesitas acceder a las claves y no a los valores.</p>

<p><strong>Ejemplo:</strong><br>
</p>

<pre class="brush:php;toolbar:false">const obj = { a: 1, b: 2, c: 3 };
const claves = Object.keys(obj);
console.log(claves); // ["a", "b", "c"]

這種方法更乾淨、更容易閱讀,特別是當您處理大型或複雜的物件時。

11.使用Map作為非原始鍵

當需要將值與非字串或符號的鍵關聯時,請使用Map。它更加健壯,並且保持了鍵的類型和順序。

範例:

const producto = {};
const impuesto = producto?.precio?.impuesto;
console.log(impuesto); // undefined

12.使用Symbol作為唯一鍵

符號是一項 JavaScript 功能,可讓您建立唯一且不可變的鍵,當我們需要確保值不會意外覆寫或存取時,符號是一個強大的工具。符號無法透過 Object.keys()、for...in 或 JSON.stringify() 等方法訪問,這使得它們非常適合私有或「隱藏」值。

為什麼要使用符號?

當我們使用文字字串等鍵來建立物件的屬性時,可以輕鬆操作或覆寫它們。然而,即使我們創建同名的符號,符號也能確保每個按鍵都是唯一的。此外,符號不會出現在物件屬性枚舉中。

基本範例:

const productos = [
  { nombre: 'Laptop', detalles: { precio: 1000 } },
  { nombre: 'Teléfono', detalles: null },
  { nombre: 'Tablet', detalles: { precio: 500, impuesto: 50 } }
];

// Acceso seguro a la propiedad 'impuesto' de cada producto
productos.forEach(producto => {
  const impuesto = producto?.detalles?.impuesto;
  console.log(impuesto); // undefined, null o el valor real
});

在此範例中,hiddenKey 鍵是唯一的,儘管我們程式碼的另一部分可以建立另一個 Symbol('hidden'),但它會完全不同,並且不會影響儲存在 obj 中的值。

進階範例:將 Symbol 與 Object.defineProperty 組合

您甚至可以將 Symbol 與 Object.defineProperty 一起使用,以更受控制的方式為物件新增屬性,確保屬性是不可枚舉的。

const usuario = { nombre: 'Juan', obtenerEdad: null };
const edad = usuario.obtenerEdad?.();
console.log(edad); // undefined

在此範例中,secretKey 不會出現在物件的鍵枚舉中,這使其成為不應意外存取或修改的「私有」值的理想選擇。

注意事項:

  • 符號對於避免屬性名稱衝突非常有用,尤其是在使用第三方函式庫或 API 時。
  • 雖然符號並不是嚴格意義上的“私有”,但它們不可按常規方式訪問的事實有助於保護資料的完整性。
  • 請注意,Symbol 無法序列化為 JSON,因此如果需要傳輸數據,請務必正確處理 Symbol 屬性。

13. 小心 JSON 和大數字

在 JavaScript 中,處理大量數字可能是一個真正的挑戰。 Number 資料類型在準確表示整數方面有限制:最大安全整數值為 9007199254740991(也稱為 Number.MAX_SAFE_INTEGER)。如果您嘗試使用大於此值的數字,則可能會失去精確度,這可能會導致應用程式出現錯誤。

例如,假設您從外部 API 收到大量數字:

async function obtenerDatos() {
  try {
    const respuesta = await fetch('https://api.ejemplo.com/datos');
    if (!respuesta.ok) {
      throw new Error('Error al obtener los datos');
    }
    const datos = await respuesta.json();
    console.log(datos);
  } catch (error) {
    console.error('Error:', error.message);
  }
}

如您所見,數字 9007199254740999 被錯誤地轉換為 9007199254741000。如果該數字對您的應用程式至關重要(例如唯一識別碼或財務金額),這可能會出現問題。

如何避免這個問題?

一個簡單而優雅的解決方案是使用 ECMAScript 2020 中引入的 BigInt 資料類型。 BigInt 可以處理更大的數字而不會損失精確度。但是,JSON 本身不會處理 BigInt,因此您需要在序列化數字時將其轉換為字串,然後在反序列化時將它們轉換回來。

以下是如何做到這一點的範例:

使用 BigInt 和 JSON.stringify 的解決方案

const producto = {};
const impuesto = producto?.precio?.impuesto;
console.log(impuesto); // undefined

透過使用此方法,您可以保持大量資料的準確性,而不會遺失重要資料。當您再次需要該數字時,只需將其轉換回 BigInt:

const productos = [
  { nombre: 'Laptop', detalles: { precio: 1000 } },
  { nombre: 'Teléfono', detalles: null },
  { nombre: 'Tablet', detalles: { precio: 500, impuesto: 50 } }
];

// Acceso seguro a la propiedad 'impuesto' de cada producto
productos.forEach(producto => {
  const impuesto = producto?.detalles?.impuesto;
  console.log(impuesto); // undefined, null o el valor real
});

其他策略

如果您不想使用 BigInt 或考慮效能,另一個策略是簡單地將大數字視為 JSON 中的字串。這避免了精度問題,但代價是必須在程式碼中進行轉換。

範例:

const usuario = { nombre: 'Juan', obtenerEdad: null };
const edad = usuario.obtenerEdad?.();
console.log(edad); // undefined

為什麼它很重要?

正確處理大數不僅對於計算的準確性至關重要,而且對於維護資料完整性也至關重要。當您使用您無法完全控制的第三方 API 或系統時,這一點尤其重要。錯誤解釋的數字可能會導致應用程式失敗,或更糟的是,可能會導致至關重要的資料錯誤,例如金融交易或資料庫中唯一識別碼的處理。

記住:不要忽略精度限制。雖然這看起來像是一個小細節,但應用程式可能會以意想不到的方式失敗,而且代價高昂。

14. 明確處理 if 語句中的表達式

在 JavaScript 中,if 語句隱式會將表達式轉換為「真」或「假」值,如果不考慮此行為,可能會導致意外結果。儘管這種行為有時很有用,但建議在比較中明確,以避免細微的錯誤並提高程式碼的可讀性。

「真」或「假」是什麼意思?

  • 「Falsy」 指的是條件式中求值時被視為等同於 false 的值。範例:0、「」(空字串)、null、未定義、NaN。
  • 「Truthy」 是所有非 falsy 的值,即不是上述之一的任何值。範例:0 以外的任何數字、任何非空字串、物件等

隱式範例(可能會給出意想不到的結果)

const producto = {};
const impuesto = producto?.precio?.impuesto;
console.log(impuesto); // undefined

在上面的例子中,條件沒有被執行,因為 0 被認為是「假」。然而,當使用更複雜的值時,這種行為可能很難檢測到。

明確的範例(更好的可讀性)

const productos = [
  { nombre: 'Laptop', detalles: { precio: 1000 } },
  { nombre: 'Teléfono', detalles: null },
  { nombre: 'Tablet', detalles: { precio: 500, impuesto: 50 } }
];

// Acceso seguro a la propiedad 'impuesto' de cada producto
productos.forEach(producto => {
  const impuesto = producto?.detalles?.impuesto;
  console.log(impuesto); // undefined, null o el valor real
});

提示:每當您處理可能為 false 的值(例如 0、null、false 或「」)時,最好在比較中明確。這樣您就可以確保邏輯按照您的期望執行,而不是因為隱式類型強制行為。

另一個值不明確的例子

假設您有一個可以為 null 的物件、一個空數組 [] 或一個空物件 {}。如果你這樣做:

const usuario = { nombre: 'Juan', obtenerEdad: null };
const edad = usuario.obtenerEdad?.();
console.log(edad); // undefined

雖然 [] (空數組)是一個有效且真實的對象,但如果您不完全理解其行為,它可能會在將來導致混亂。與其依賴隱式強制轉換,最好進行更明確的比較,例如:

async function obtenerDatos() {
  try {
    const respuesta = await fetch('https://api.ejemplo.com/datos');
    if (!respuesta.ok) {
      throw new Error('Error al obtener los datos');
    }
    const datos = await respuesta.json();
    console.log(datos);
  } catch (error) {
    console.error('Error:', error.message);
  }
}

為什麼它很重要?

透過明確定義條件,可以降低 JavaScript 自動強制導致錯誤的風險。這種方法使您的程式碼更清晰、更易讀、更可預測。此外,它還提高了可維護性,因為其他人(或將來的你自己)將能夠快速理解邏輯,而不必記住 JavaScript 中虛假值的隱式行為。

15. 盡可能使用嚴格相等(===)

JavaScript 最令人困惑的行為之一來自非嚴格相等運算子 (==)。該運算符執行所謂的類型強制,這意味著它在比較值之前嘗試將它們轉換為通用類型。這可能會產生出乎意料的結果並且非常難以調試。

例如:

const producto = {};
const impuesto = producto?.precio?.impuesto;
console.log(impuesto); // undefined

這是一種在你開發過程中會讓你發瘋的事情。 == 運算子將 [] (空數組)與 ![] 進行比較(結果為 false,因為 [] 被視為 true 值,而 ![] 將其轉換為 false)。然而,根據 JavaScript 的內部強制規則,這是一個有效的結果,儘管乍看之下沒有意義。

為什麼會發生這種情況?

JavaScript 在比較之前將比較的雙方轉換為通用型別。在這種情況下,與 ![] 的布林值相比,空數組 [] 變成 false。這種類型的強制是一個明顯的例子,說明了錯誤的發生是多麼微妙和難以識別。

提示:始終使用嚴格相等

為了避免這些問題,只要有可能,您應該使用嚴格相等 (===)。不同之處在於這個 運算子不執行型別強制 。這意味著它嚴格比較變數的值和類型。

const productos = [
  { nombre: 'Laptop', detalles: { precio: 1000 } },
  { nombre: 'Teléfono', detalles: null },
  { nombre: 'Tablet', detalles: { precio: 500, impuesto: 50 } }
];

// Acceso seguro a la propiedad 'impuesto' de cada producto
productos.forEach(producto => {
  const impuesto = producto?.detalles?.impuesto;
  console.log(impuesto); // undefined, null o el valor real
});

更典型的例子

以下是一些較常見的範例,說明非嚴格相等 (==) 可能會出現問題:

const usuario = { nombre: 'Juan', obtenerEdad: null };
const edad = usuario.obtenerEdad?.();
console.log(edad); // undefined

為什麼使用 === 很重要?

  • 預測與可靠性: 使用 === 可以讓您進行更可預測的比較,而不會出現意外或類型轉換。
  • 避免難以偵測的錯誤:當您的程式碼開始變得更大、更複雜時,與類型強制相關的錯誤可能很難發現和偵錯。
  • 提高程式碼可讀性:當您看到明確比較時,其他開發人員(或將來的您自己)更容易理解您的程式碼,而不會產生隱式轉換的混亂。

以上是現代 JavaScript 最佳實務 - 第 2 部分的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn