在本文的第一部分中,我们探讨了现代 JavaScript 的基础知识以及一些基本的最佳实践,以开始编写更清晰、更高效的代码。但作为开发者,我们知道总是有更多东西需要学习和改进。
在处理对象或嵌套结构时,我们有时需要在尝试访问属性之前检查它是否存在。 可选链接运算符 (?.) 是一个强大的工具,可以简化此任务,避免 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时避免错误,即使详细信息为空或不存在。
可选链也可以与函数一起使用,当您的函数可能未在对象上定义时,这非常有用:
const usuario = { nombre: 'Juan', obtenerEdad: null }; const edad = usuario.obtenerEdad?.(); console.log(edad); // undefined
这里,getAge函数是未定义的(它为空),但它不会抛出错误,它只是返回未定义。
当您在 JavaScript 中使用异步操作时,例如从 API 获取数据或读取文件,async/await 语法可能是您最好的朋友。 async/await 不是使用 .then() 和 .catch() 中的 Promise,而是允许您以更清晰、更易读的方式编写异步代码,类似于我们编写同步代码的方式。
假设我们正在使用一个返回数据的 API。使用 async/await 而不是 .then() 使流程更容易遵循:
const producto = {}; const impuesto = producto?.precio?.impuesto; console.log(impuesto); // undefined
假设您有一个网页,您需要在其中显示来自 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 });
返回一个包含对象属性的所有值的数组。当您只需要值而不需要键时,这是完美的。
示例:
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); } }
您是否知道可以将这些方法与 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"]
这种方法更干净、更容易阅读,特别是当您处理大型或复杂的对象时。
当需要将值与非字符串或符号的键关联时,请使用Map。它更加健壮,并且保持了键的类型和顺序。
示例:
const producto = {}; const impuesto = producto?.precio?.impuesto; console.log(impuesto); // undefined
符号是一项 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 一起使用,以更受控制的方式向对象添加属性,确保属性是不可枚举的。
const usuario = { nombre: 'Juan', obtenerEdad: null }; const edad = usuario.obtenerEdad?.(); console.log(edad); // undefined
在此示例中,secretKey 不会出现在对象的键枚举中,这使其成为不应意外访问或修改的“私有”值的理想选择。
在 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,因此您需要在序列化数字时将其转换为字符串,然后在反序列化时将它们转换回来。
以下是如何做到这一点的示例:
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 或系统时,这一点尤其重要。错误解释的数字可能会导致应用程序失败,或更糟糕的是,可能会导致至关重要的数据错误,例如金融交易或数据库中唯一标识符的处理。
记住:不要忽略精度限制。虽然这看起来像是一个小细节,但应用程序可能会以意想不到的方式失败,并且代价高昂。
在 JavaScript 中,if 语句隐式将表达式转换为“真”或“假”值,如果不考虑此行为,可能会导致意外结果。尽管这种行为有时很有用,但建议在比较中明确,以避免细微的错误并提高代码的可读性。
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 中虚假值的隐式行为。
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中文网其他相关文章!