Dar sentido a las funciones de JavaScript "sin sentido"

 

 

 

  • Implemente rápidamente. Implementar inteligentemente
  • Clase magistral de tipografía, con Elliot Jay Stocks

  • Índice
    1. 0.1 + 0.2Y el formato de coma flotante
    2. Tipo Coerción
    3. Inserción automática de punto y coma
    4. ¿Por qué tantos valores inferiores?
    5. Incremento ( ++) y decremento ( --)
    6. Conclusión

    Puede que JavaScript sea el lenguaje del lado del cliente más popular del mundo, pero está lejos de ser perfecto y no está exento de peculiaridades. Juan Diego Rodríguez examina varias excentricidades "absurdas" de JavaScript y explica cómo llegaron al lenguaje y cómo evitarlas en su propio código.

     

    ¿Por qué JavaScript tiene tantas excentricidades? ¿Por qué es 0.2 + 0.1igual 0.30000000000000004? O, ¿por qué se "" == falseevalúa como true?

    Hay muchas decisiones alucinantes en JavaScript que parecen inútiles; algunos se malinterpretan, mientras que otros son errores directos de diseño. De todos modos, vale la pena saber qué son estas cosas extrañas y por qué están en el idioma. Compartiré lo que creo que son algunas de las cosas más extravagantes sobre JavaScript y les daré sentido.

    0.1 + 0.2Y el formato de coma flotante

    Muchos de nosotros nos hemos burlado de JavaScript escribiendo 0.1 + 0.2en la consola y viendo cómo falla rotundamente 0.3, sino más bien un valor de apariencia divertida 0.30000000000000004.

    Lo que muchos desarrolladores quizás no sepan es que el extraño resultado no es realmente culpa de JavaScript. JavaScript simplemente se adhiere al estándar IEEE para aritmética de punto flotante que casi todas las demás computadoras y lenguajes de programación utilizan para representar números.

    Pero ¿qué es exactamente la aritmética de coma flotante?

    Las computadoras tienen que representar números de todos los tamaños, desde la distancia entre planetas e incluso entre átomos. En papel, es fácil escribir un número enorme o una cantidad minúscula sin preocuparse por el tamaño que ocupará. Las computadoras no pueden darse ese lujo ya que tienen que guardar todo tipo de números en binario y un pequeño espacio en la memoria.

    Tomemos, por ejemplo, un entero de 8 bits. En binario, puede contener números enteros que van desde 0hasta 255.

    Enteros de 8 bits que muestran 0 y 255. ( Vista previa grande )

    La palabra clave aquí es números enteros . No puede representar ningún decimal entre ellos. Para solucionar este problema, podríamos agregar un punto decimal imaginario en algún lugar de nuestros 8 bits, de modo que los bits antes del punto se usen para representar la parte entera y el resto se use para la parte decimal. Como el punto siempre está en el mismo lugar imaginario, se llama punto decimal fijo . Pero tiene un gran coste ya que el rango se reduce de 0a255 a exactamente 0a15.9375 .

     

    Decimales con punto fijo. ( Vista previa grande )

    Tener mayor precisión significa sacrificar alcance, y viceversa. También debemos tener en cuenta que las computadoras deben satisfacer a una gran cantidad de usuarios con diferentes requisitos. Un ingeniero que construye un puente no se preocupa demasiado si las medidas están ligeramente desviadas, digamos una centésima de centímetro. Pero, por otro lado, esa misma centésima de centímetro puede acabar costando mucho más a alguien que fabrique un microchip. La precisión que se necesita es diferente y las consecuencias de un error pueden variar.

    Otra consideración es el tamaño donde se almacenan los números en la memoria, ya que no es factible almacenar números largos en algo así como un megabyte.

    El formato de punto flotante nació de esta necesidad de representar cantidades grandes y pequeñas con precisión y eficiencia. Lo hace en tres partes:

    1. Un solo bit que representa si el número es positivo o negativo ( 0para positivo, 1para negativo).
    2. Un significado o mantisa que contiene los dígitos del número.
    3. Un exponente especifica dónde se coloca el punto decimal (o binario) con respecto al comienzo de la mantisa, de forma similar a cómo funciona la notación científica. En consecuencia, el punto puede moverse a cualquier posición, de ahí el punto flotante .

    Decimales con punto flotante. ( Vista previa grande )

    Un formato de punto flotante de 8 bits puede representar números entre 0.0078a 480(y sus negativos), pero observe que la representación de punto flotante no puede representar todos los números en ese rango. Es imposible ya que 8 bits pueden representar sólo 256 valores distintos. Inevitablemente, muchos números no pueden representarse con precisión. Hay lagunas a lo largo de la gama. Las computadoras, por supuesto, trabajan con más bits para aumentar la precisión y el alcance, comúnmente con 32 bits y 64 bits, pero es imposible representar todos los números con precisión, un pequeño precio a pagar si consideramos el rango que ganamos y la memoria que utilizamos. ahorrar.

    La dinámica exacta es mucho más compleja, pero por ahora sólo tenemos que entender que si bien este formato nos permite expresar números en un rango grande, pierde precisión (los espacios entre valores representables se hacen más grandes) cuando se vuelven demasiado grandes. Por ejemplo, los números de JavaScript se presentan en un formato de punto flotante de doble precisión, es decir, cada número se representa en 64 bits en la memoria, dejando 53 bits para representar la mantisa. Eso significa que JavaScript solo puede representar de forma segura números enteros entre –(2 53 — 1) y 2 53 — 1 sin perder precisión. Más allá de eso, la aritmética deja de tener sentido. Es por eso que tenemos la Number.MAX_SAFE_INTEGERpropiedad de datos estáticos para representar el entero máximo seguro en JavaScript, que es (2 53 — 1) o 9007199254740991.

     

    Pero 0.3obviamente está por debajo del MAX_SAFE_INTEGERumbral, entonces, ¿por qué no podemos obtenerlo al agregar 0.1y 0.2? El formato de punto flotante tiene problemas con algunos números fraccionarios. No es un problema con el formato de punto flotante, pero ciertamente lo es en cualquier sistema numérico.

    Para ver esto, representemos un tercio ( 13 ) en base 10.

    0.3
    0.33
    0.3333333 [...]

    No importa cuántos dígitos intentemos escribir, el resultado nunca será exactamente un tercio. De la misma manera, no podemos representar con precisión algunos números fraccionarios en base 2 o binario. Toma por ejemplo, 0.2. Podemos escribirlo sin problema en base-10, pero si intentamos escribirlo en binario obtenemos un recurrente 1001al final que se repite infinitamente.

    0.001 1001 1001 1001 1001 1001 10 [...]

    Obviamente no podemos tener un número infinitamente grande, por lo que en algún momento la mantisa debe truncarse, lo que hace imposible no perder precisión en el proceso. Si intentamos convertir 0.2de punto flotante de doble precisión a base 10, veremos el valor real guardado en la memoria:

    0.200000000000000011102230246251565404236316680908203125

    ¡No es 0,2! No podemos representar una gran cantidad de valores fraccionarios, no sólo en JavaScript sino en casi todas las computadoras. Entonces, ¿por qué se 0.2 + 0.2calcula si se ejecuta correctamente 0.4? En este caso, la imprecisión es tan pequeña que Javascript la redondea (al decimosexto decimal ), pero a veces la imprecisión es suficiente para escapar del mecanismo de redondeo, como es el caso de 0.2 + 0.1. Podemos ver lo que sucede bajo el capó si intentamos sumar los valores reales de 0.1y 0.2.

    Este es el valor real guardado al escribir 0.1:

    0.1000000000000000055511151231257827021181583404541015625

    Si sumamos manualmente los valores reales de 0.1y 0.2, veremos al culpable:

    0.3000000000000000444089209850062616169452667236328125

    Ese valor se redondea a 0.30000000000000004. Puede comprobar los valores reales guardados en float.exposed .

    El punto flotante tiene sus defectos conocidos, pero sus aspectos positivos los superan y es estándar en todo el mundo. En ese sentido, es realmente un alivio que todos los sistemas modernos nos den el mismo 0.30000000000000004resultado en todas las arquitecturas. Puede que no sea el resultado que espera, pero es un resultado que puede predecir.

    Tipo Coerción

    JavaScript es un lenguaje de tipo dinámico, lo que significa que no tenemos que declarar el tipo de variable y se puede cambiar más adelante en el código.

    Los lenguajes tipados dinámicamente me parecen liberadores, ya que podemos centrarnos más en la esencia del código.

    El problema proviene de estar mal tipado, ya que hay muchas ocasiones en las que el lenguaje intentará realizar una conversión implícita entre diferentes tipos, por ejemplo, de cadenas a números o valores falsos y verdaderos . Esto es específicamente cierto cuando se utilizan los operadores de igualdad ( ==) y signo más ( +). Las reglas para la coerción de tipos son complejas, difíciles de recordar e incluso incorrectas en determinadas situaciones. Es mejor evitar el uso ==y preferir siempre el operador de igualdad estricta ( ===).

    Por ejemplo, JavaScript convertirá una cadena en un número en comparación con otro número:

    console.log("2" == 2); // true

    Lo inverso se aplica al operador del signo más ( +). Intentará convertir un número en una cadena cuando sea posible:

    console.log(2 + "2"); // "22"

    Por eso solo debemos usar el operador de signo más ( +) si estamos seguros de que los valores son números. Al concatenar cadenas, es mejor utilizar el concat()método o los literales de plantilla .

    La razón por la que tales coacciones están en el lenguaje es realmente absurda. Cuando se le preguntó al creador de JavaScript, Brendan Eich, qué habría hecho diferente en el diseño de JavaScript, su respuesta fue ser más meticuloso en las implementaciones que querían los primeros usuarios del lenguaje: Guia y trucos de Tiktok

    “Habría evitado algunos de los compromisos que hice cuando conseguí a los primeros usuarios y me dijeron: “¿Puedes cambiar esto?”

    —Brendan Eich

    El ejemplo más evidente es la razón por la que tenemos dos operadores de igualdad, ==y ===. Cuando uno de los primeros usuarios de JavaScript le planteó la necesidad de comparar un número con una cadena sin tener que cambiar su código para realizar una conversión, Brendan agregó el operador de igualdad flexible para satisfacer esas necesidades.

    Hay muchas otras reglas que rigen el operador de igualdad flexible (y otras declaraciones que verifican una condición) que hacen que los desarrolladores de JavaScript se rasquen la cabeza. Son complejos, tediosos y sin sentido, por lo que debemos evitar el operador de igualdad flexible ( ==) a toda costa y reemplazarlo con su homónimo estricto ( ===).

    ¿Por qué tenemos dos operadores de igualdad en primer lugar? Hay muchos factores, pero podemos señalar a Guy L. Steele, cocreador del lenguaje de programación Scheme. ¡Le aseguró a Eich que siempre podríamos agregar otro operador de igualdad ya que había dialectos con cinco operadores de igualdad distintos en el lenguaje Lisp! Esta mentalidad es peligrosa y hoy en día todas las características deben analizarse rigurosamente porque siempre podemos agregar nuevas características, pero una vez que están en el lenguaje, no se pueden eliminar.

     

    Inserción automática de punto y coma

    Al escribir código en JavaScript, ;se requiere un punto y coma ( ) al final de algunas declaraciones, incluidas:

    • var, let, const;
    • Declaraciones de expresión;
    • do...while;
    • continue, break, return, throw;
    • debugger;
    • Declaraciones de campos de clase (públicas o privadas);
    • import, export.

    Dicho esto, no necesariamente tenemos que insertar un punto y coma cada vez, ya que JavaScript puede insertar punto y coma automáticamente en un proceso conocido como Inserción Automática de Punto y Coma (ASI). Su objetivo era facilitar la codificación a los principiantes que no sabían dónde se necesitaba un punto y coma, pero no es una característica confiable y deberíamos limitarnos a escribir explícitamente dónde va un punto y coma. Los linters y formateadores añaden un punto y coma donde lo haría ASI, pero tampoco son completamente confiables.

    ASI puede hacer que algún código funcione, pero la mayoría de las veces no es así. Tome el siguiente código:

    const a = 1(1).toString()const b = 1[1, 2, 3].forEach(console.log)

    Probablemente puedas ver dónde van los puntos y coma y, si lo formateamos correctamente, terminará como:

    const a = 1;(1).toString();const b = 1;[(1, 2, 3)].forEach(console.log);

    Pero si alimentamos el código anterior directamente a JavaScript, se producirían todo tipo de excepciones ya que sería lo mismo que escribir esto:

    const a = 1(1).toString();const b = (1)[(1, 2, 3)].forEach(console.log);

    En conclusión, conozca su punto y coma.

    ¿Por qué tantos valores inferiores?

    El término "fondo" se utiliza a menudo para representar un valor que no existe o no está definido. Pero ¿por qué tenemos dos tipos de valores inferiores en JavaScript?

    Todo en JavaScript puede considerarse un objeto, excepto los dos valores inferiores nully undefined(a pesar de typeof nulldevolver object). Intentar obtener de ellos el valor de una propiedad genera una excepción.

    Tenga en cuenta que, estrictamente hablando, todos los valores primitivos no son objetos . Pero sólo nully undefinedno están sujetos al boxeo .

    Incluso podemos pensar en NaNun tercer valor inferior que representa la ausencia de un número. La abundancia de valores inferiores debe considerarse como un error de diseño. No hay una razón sencilla que explique la existencia de dos valores inferiores, pero podemos ver una diferencia en cómo JavaScript los emplea.

    undefinedes el valor inferior que JavaScript usa de forma predeterminada, por lo que se considera una buena práctica usarlo exclusivamente en su código. Cuando definimos una variable sin un valor inicial, al intentar recuperarla se asigna el undefinedvalor. Lo mismo sucede cuando intentamos acceder a una propiedad inexistente desde un objeto. Para coincidir lo más posible con el comportamiento de JavaScript, utilice undefinedpara indicar una propiedad o variable existente que no tiene un valor.

     

    Por otro lado, nullse usa para representar la ausencia de un objeto (por lo tanto, devuelve typeofun objectaunque no lo sea). Sin embargo, esto se considera un error de diseño porque undefinedpodría cumplir sus propósitos con la misma eficacia. JavaScript lo utiliza para indicar el final de una estructura de datos recursiva. Más específicamente, se utiliza en la cadena del prototipo para indicar su final. La mayoría de las veces se puede usar undefinedover null, pero hay algunas ocasiones en las que only nullse puede usar, como es el caso en Object.createel que solo podemos crear un objeto sin pasar un prototipo null; usando undefineddevuelve un TypeError.

    nully undefinedambos sufren el problema de la ruta. Al intentar acceder a una propiedad desde un valor inferior, como si fueran objetos, se generan excepciones.

    let user;let userName = user.name; // Uncaught TypeErrorlet userNick = user.name.nick; // Uncaught TypeError

    No hay forma de evitar esto a menos que verifiquemos cada valor de propiedad antes de intentar acceder al siguiente, ya sea usando el AND lógico ( ) o el encadenamiento opcional ( ?).

    let user;let userName = user?.name;let userNick = user user.name user.name.nick;console.log(userName); // undefinedconsole.log(userNick); // undefined

    Dije que NaNpuede considerarse un valor inferior, pero tiene su propio lugar confuso en JavaScript ya que representa números que no son números reales, generalmente debido a una conversión fallida de cadena a número (que es otra razón para evitarlo). . NaN¡Tiene sus propias travesuras porque no es igual a sí mismo! Para probar si un valor lo es NaNo no, use Number.isNaN().

    Podemos verificar los tres valores inferiores con la siguiente prueba:

    function stringifyBottom(bottomValue) { if (bottomValue === undefined) { return "undefined"; } if (bottomValue === null) { return "null"; } if (Number.isNaN(bottomValue)) { return "NaN"; }}

    Incremento ( ++) y decremento ( --)

    Como desarrolladores, tendemos a dedicar más tiempo a leer código que a escribirlo. Ya sea que estemos leyendo documentación, revisando el trabajo de otra persona o revisando el nuestro, la legibilidad del código aumentará nuestra productividad por encima de la brevedad . En otras palabras, la legibilidad ahorra tiempo a largo plazo.

    Por eso prefiero usar + 1o - 1en lugar de los operadores de incremento ( ++) y decremento ( --).

    Es ilógico tener una sintaxis diferente exclusivamente para incrementar un valor en uno además de tener una forma de pre-incremento y una forma de post-incremento, dependiendo de dónde se coloque el operador. Es muy fácil revertirlos y eso puede ser difícil de depurar. No deberían tener un lugar en su código o incluso en el lenguaje en su conjunto cuando consideramos de dónde provienen los operadores de incremento.

     

    Como vimos en un artículo anterior , la sintaxis de JavaScript está fuertemente inspirada en el lenguaje C, que utiliza variables de puntero. Las variables de puntero se diseñaron para almacenar las direcciones de memoria de otras variables, lo que permite la asignación y manipulación dinámica de la memoria. Los operadores ++y --fueron creados originalmente con el propósito específico de avanzar o retroceder a través de ubicaciones de memoria.

    Hoy en día, se ha demostrado que la aritmética de punteros es dañina y puede causar acceso accidental a ubicaciones de memoria más allá de los límites previstos de matrices o buffers, lo que genera errores de memoria, una fuente notoria de errores y vulnerabilidades. Independientemente, la sintaxis llegó a JavaScript y permanece allí hoy.

    Si bien el uso de ++y --sigue siendo un estándar entre los desarrolladores, se puede argumentar a favor de la legibilidad. Optar por + 1o - 1no ++y --no sólo se alinea con los principios de claridad y expresividad, sino que también evita tener que lidiar con su forma previa y posterior al incremento.

    En general, no es una situación de vida o muerte, sino una buena manera de hacer que su código sea más legible.

    Conclusión

    Las características aparentemente sin sentido de JavaScript a menudo surgen de decisiones históricas, compromisos e intentos de satisfacer todas las necesidades. Desafortunadamente, es imposible hacer felices a todos y JavaScript no es una excepción.

    JavaScript no tiene la responsabilidad de adaptarse a todos los desarrolladores, pero cada desarrollador tiene la responsabilidad de comprender el lenguaje y aprovechar sus puntos fuertes siendo consciente de sus peculiaridades.

    Espero que le resulte útil seguir aprendiendo más y más sobre JavaScript y su historia para comprender sus características incomprendidas y sus decisiones cuestionables. Tomemos como ejemplo su asombrosa naturaleza prototípica. Quedó oculto durante el desarrollo o por errores garrafales como la thispalabra clave y su comportamiento multipropósito.

    De cualquier manera, animo a todos los desarrolladores a investigar y aprender más sobre el lenguaje. Y si está interesado, profundizo un poco más en áreas cuestionables del diseño de JavaScript en otro artículo publicado aquí en Smashing Magazine .

    (gg, yk)Explora más en

    • javascript
    • Reaccionar





    Tal vez te puede interesar:

    1. ¿Deberían abrirse los enlaces en ventanas nuevas?
    2. 24 excelentes tutoriales de AJAX
    3. 70 técnicas nuevas y útiles de AJAX y JavaScript
    4. Más de 45 excelentes recursos y repositorios de fragmentos de código

    Dar sentido a las funciones de JavaScript "sin sentido"

    Dar sentido a las funciones de JavaScript "sin sentido"

    Implemente rápidamente. Implementar inteligentemente Clase magistral de tipografía, con Elliot Jay Stocks Índice

    programar

    es

    https://pseint.es/static/images/programar-dar-sentido-a-las-funciones-de-javascript-sin-sentido-1180-0.jpg

    2024-04-04

     

    Dar sentido a las funciones de JavaScript "sin sentido"
    Dar sentido a las funciones de JavaScript "sin sentido"

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20