首页 > web前端 > js教程 > 现代 JavaScript 最佳实践 - 第 2 部分

现代 JavaScript 最佳实践 - 第 2 部分

Patricia Arquette
发布: 2024-12-08 16:09:12
原创
684 人浏览过

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中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板