Descubriendo objetos primitivos en JavaScript (Parte 1)

 

 

 

  • Patrones de diseño de interfaces inteligentes, vídeo de 10h + formación UX
  • Patrones de diseño de interfaces inteligentes, vídeo de 10h + formación UX

  • Índice
    1. Propiedades de los valores primitivos que necesitamos
    2. Son objetos hasta el final, incluso si no lo son
    3. Cómo acercar objetos regulares a valores primitivos
    4. Mudado
      1. Representación de cadenas
      2. Para los numéricos
    5. Conclusión

    Al intentar reescribir partes de un código base irregular, Kirill ha redescubierto por sí mismo el poder de los objetos JavaScript. Siguió volviendo una y otra vez a un patrón que mantenía el código claro y eliminaba operaciones innecesarias. Acercar los objetos normales a valores primitivos marcó la diferencia. Así se descubrieron los Objetos Primitivos. En la primera parte, verás en qué se diferencian los objetos y los valores primitivos y cómo acercarlos.

     

    Parece natural utilizar cadenas para distinguir cosas. Es muy probable que en tu código base haya objetos con name, ido labelpropiedades que se utilizan para determinar si un objeto es el que estás buscando.

    if (element.label === "title") { make_bold(element);}

    En cierto punto, tu proyecto crece (en tamaño, importancia, popularidad o todo a la vez). Necesita más cadenas ya que hay más cosas que distinguir entre sí. Las cadenas se hacen más largas, al igual que el costo de los errores tipográficos o, por ejemplo, los cambios en la convención de nomenclatura de las etiquetas. Ahora tienes que encontrar todas las instancias de esas cadenas y reemplazarlas. En consecuencia, el compromiso para ese cambio se vuelve mucho mayor de lo que debería ser. Lo que te hace quedar mejor ante los ojos de los despistados. Al mismo tiempo, te hace la vida imposible, ya que ahora es mucho más difícil encontrar la causa de la regresión en tu historial de git.

    Las cadenas son malas para la identificación. Hay que tener en cuenta la unicidad y los errores tipográficos; su editor o IDE no comprobará si es la cadena que quería decir. Es malo. Escucho a alguien decir: "Ponlos en una variable, claro". Esa es una buena sugerencia y elimina algunas de mis preocupaciones. Pero mire a John Smith:

    const john_smith_a_person = "John Smith";const john_smith_a_company = "John Smith";// Do they have the same name?john_smith_a_person === john_smith_a_company; // true// Are they the same thing?john_smith_a_person === john_smith_a_company; // true

    Resulta que John comparte el nombre con una empresa. ¿Qué pasa si te digo que tengo una solución mejor? El que elimina todas las preocupaciones y agrega más valor le permite lograr más. ¿Qué dirías? Bueno, no reescribiré el artículo sólo porque tu respuesta no se ajuste a mi narrativa. La respuesta son los objetos. Usas los propios objetos para determinar si un objeto es el que estás buscando.

     

    // Do they have a same name?john_smith_a_person.name === john_smith_a_company.name; // true// Are they the same thing?john_smith_a_person === john_smith_a_company; // false

    Hace que la intención sea más clara. Déjame darte un mejor ejemplo. Digamos que tienes etiquetas en tu aplicación. Están localizados, por lo que la cadena de etiqueta está determinada por la biblioteca de localización que esté utilizando y el proceso de traducción de su equipo. Mantienes tus etiquetas en un módulo donde las tienes todas perfectamente organizadas y seleccionadas. Una vez que necesites hacer algo especial para ciertas etiquetas, puedes compararlo directamente con el que tienes.

    import React from "react";import labels from "./labels.js";const render_label(label) = ( Label className={label === labels.title ? "bold" : "plain"} icon={label.icon} text={label.text} /)function TableOfContents({ items }) { return ( ul className="my-menu" {items.map(render_label(item.label)} /ul );}

    ¿Ves cuánto más puedo hacer con los objetos? En el labelsmódulo, he reservado una etiqueta title, que en este caso debería estar en negrita. Además, al ser un objeto, mi etiqueta puede contener una cadena de localización (llamada imaginativamente text) y un ícono. Todo está perfectamente organizado de antemano, lo que mantiene limpia la lógica de mi interfaz de usuario.

    Pero es sólo una parte del panorama. Sé que usamos objetos en todas partes y no es nada nuevo agrupar cosas en ellos. Pero apuesto a que no los usas exactamente así. Rara vez veo dos objetos comparados de esa manera porque nunca se sabe qué hay allí ni de dónde viene. Los objetos se crean y cambian todo el tiempo. Es más probable que se los compare por los valores de sus propiedades que por los objetos mismos. Y la razón es que los objetos no son aptos para ese tipo de uso. Son demasiado capaces. Para permitir ese caso de uso y muchos otros, tenemos que, por un lado, reducir algunas capacidades de los objetos y, por el otro, implementar algunas más. Y al final obtendremos lo que yo llamo Objetos Primitivos . Esa... una solución a todos... algunos problemas.

    En la primera parte de la serie, quiero cubrir algunos aspectos de JavaScript que ayudan a acercar los objetos a valores primitivos, lo que a cambio nos permitiría beneficiarnos de características comunes del lenguaje que normalmente no están asociadas con un objeto, como comparaciones y operadores aritméticos. En la siguiente parte, veremos detenidamente ejemplos prácticos y herramientas para trabajar con dichos objetos. Ahora veamos cómo son los objetos en JavaScript.

    Propiedades de los valores primitivos que necesitamos

    Primero, definamos nuestro objetivo. Hagamos un dibujo de dónde nos gustaría estar después. ¿Qué propiedades de los valores primitivos queremos que tengan nuestros objetos?

    • Inmutabilidad
      Los valores primitivos son de sólo lectura. Queremos que nuestros objetos no sean editables por nadie después de su creación. Recuerde el ejemplo anterior. ¿Qué utilidad tiene una etiqueta si algún código fuera de nuestro control ha cambiado el texto o icono de la misma? Una vez definido el objeto, debe quedar escrito en piedra.
    • Trabajar con operadores.
      Las expresiones con ciertos operadores devuelven su tipo apropiado. Los operadores aritméticos devuelven números. Las comparaciones dan valores booleanos.
    • Tener sintaxis literal.
      Los literales de las primitivas te dan el valor exacto, o más bien un objeto que representa el valor. Estos objetos se crean una vez para cada valor. Cada vez que tienes "hello"en tu código, obtienes el mismo objeto.
    • Tener tipos.
      El typeofoperador le dice con qué está tratando (excepto null). No siempre sabemos qué tipo de objeto obtenemos. Entonces, antes de profundizar en sus propiedades, sería bueno saber a qué nos enfrentamos.

    Los enumeré por utilidad inmediata. Y por suerte, también están ordenados por los más fáciles de conseguir. En este artículo cubriré el primero y una parte del segundo. Veremos cómo hacer que los objetos sean inmutables. También definiremos su representación en valores primitivos, lo que nos permite utilizar algunos operadores sobre ellos. Pasar de objetos a valores primitivos es fácil, ya que los valores primitivos son objetos en sí mismos, más o menos.

    Son objetos hasta el final, incluso si no lo son

    Recuerdo mi confusión cuando lo vi por primera vez {} === {}; // false. ¿Qué es ese lenguaje que ni siquiera puede distinguir dos cosas iguales? Se sentía tan ridículo y divertido. Mucho más tarde descubrí que hay partes mucho peores en JavaScript, después de lo cual dejé de reír mientras miraba lo que hablaba .

    Un objeto es una de las cosas fundamentales en JavaScript. Es posible que hayas escuchado que en JavaScript todo es un objeto. Eso es bastante cierto. Aparte de algunos valores inferiores, todas las primitivas son objetos. Si bien técnicamente tiene más matices, desde la perspectiva de nuestro código, es cierto. De hecho, es bastante cierto que creer que todo es un objeto podría ser un modelo mental útil. Pero primero intentemos entender qué está pasando con esa comparación de objeto a objeto que tanto me divertía a mí, más joven.

    La sintaxis literal de objeto se utiliza para crear nuevos objetos. Nos permite declarar e iniciar un objeto en una sola expresión.

     

    // Instead of this.const my_object = new Object();my_object.first_property = "First property";my_object.nth_property = "Next property";// You can do this.const my_object = { first_property: "First property", nth_property: "Next property"};

    Mucho más limpio, ¿verdad? Pero ahora creo que la falta de una línea de inicialización de objetos es lo que me confundió acerca de esas dos expresiones de igualdad de objetos vacías. Parecía mostrar la lucha del idioma por reconocer una igualdad aparente. Pero lo que realmente sucede en esa expresión es esto:

    new Object() === new Object(); // false

    Ahora es obvio que no son iguales. Estás comparando dos objetos distintos que acabas de crear. Esperar lo contrario es lo mismo que esperar 5 === 3regresar true. En ambos casos son cosas distintas.

    Hagamos un control de cordura. ¿Se considerarían iguales dos variables referidas al mismo objeto?

    const my_object = {};const other_thing = my_object;my_object === other_thing; // true

    En este caso, sólo la primera línea tiene una expresión que crea un objeto. En la segunda línea, hacemos que la other_thingvariable se refiera a un objeto recién creado. Ahora dos variables se refieren al mismo objeto. Compararlos es como comparar dos números iguales, ¿no?

    ¿Por qué es esto significativo? Porque nos da una manera de comprobar si una variable hace referencia a un objeto que estamos buscando. Y si lo pensamos en el contexto de “todo es un objeto”, así es como funcionan los números y las cadenas. Cuando comparas dos variables que contienen cadenas, el motor no tiene que comprobar si cada carácter de esas cadenas es el mismo. Basta comparar si las variables se refieren al mismo objeto. Esto se debe a la diferencia más significativa entre los objetos regulares y los valores primitivos: la inmutabilidad.

    Cómo acercar objetos regulares a valores primitivos

    En JavaScript, los valores primitivos son inmutables. No se puede cambiar un solo carácter en una cadena y tampoco se puede convertir un número cinco en seis. Si utiliza constpara inicializar una variable y ponerle un valor primitivo, siempre permanecerá igual. Nadie podría cambiar el valor; es inmutable. Nadie pudo reasignar la variable; fue creado con const.

    Miremos de cerca cómo funcionan los números. Puedes obtener seis de cinco incrementándolo en uno, pero eso no cambia nada acerca de cinco.

    const five = 5;const six = 5 + 1;five === 5; // true

    Algunos podrían decir que usar letcambiaría eso. Pero mira, no puede cambiar cinco:

    const five = 5;let result = 5;result++;result === 6; // truefive === 5; // true

    Un cinco sigue siendo un cinco. Esto se debe a ++que es sólo una abreviatura de += 1. ¿Ves el signo igual? Lo que pasó fue que asigné un nuevo valor a la resultvariable, el valor que obtuve de la result + 1expresión (que es += 1una abreviatura de). La constpalabra clave impide la reasignación a una variable. En el ejemplo anterior, eso es lo que me da una forma de saber que fivesiempre se refiere a un 5objeto. Mejores Opiniones y reviews

    Podríamos suponer que la única forma en que se cambian los valores primitivos en JavaScript es mediante la asignación, lo que significa que lo que realmente estamos cambiando es a qué se refiere una variable. Entonces son las variables las que están cambiando, no los valores. Al menos no los primitivos. ¿Pero cómo funciona con objetos?

     

    Después de inicializar un objeto, puede cambiar sus propiedades: eliminarlas, agregar otras nuevas y reasignar las antiguas. Todos estamos familiarizados con hacer eso. Pero aparte de eso, se comporta igual que los valores primitivos. De hecho, si te acostumbras a un modelo donde los objetos y los valores primitivos son lo mismo, verás de manera diferente todo tipo de problemas en JavaScript.

    Probablemente te hayas topado con una pregunta sobre cómo se pasan las variables a una función. La gente pregunta si las variables se pasan por valor o por referencia. Una respuesta común es que los valores primitivos se pasan por valor, mientras que los objetos se pasan por referencia. Pero con el modelo mental que te estoy imponiendo aquí, es posible que ya sepas lo que diré al respecto. Antes de eso, déjame mostrarte cómo la pregunta no tiene mucho sentido en JavaScript. También te revelaré un juego de manos que utilizan muchos artículos y tutoriales.

    Cuando pasa variables como parámetros de una llamada a una función, se asignan a los argumentos de la función. Los argumentos son variables locales para el alcance de una función y no tienen conexión con las variables originales, lo cual tiene sentido. Si pasas una expresión a una función, tienes que poner el resultado en algún lugar, ¿no?

    Mire las siguientes dos funciones. Hacen lo mismo, pasan un valor, pero uno se define con un solo parámetro y el otro con ninguno. El segundo demuestra lo que está sucediendo con el parámetro que ingresamos.

    function single(arg) { return arg;} function none() { // The first parameter is assigned to a variable `arg`. // Notice the `let`; it will be significant later. let arg = arguments[0]; return arg;} single("hi"); // "hi"none(5); // 5

    Ya ves que ambos funcionan igual. Teniendo en cuenta cómo funcionan los argumentos de función, intentemos cambiar algunos valores. Tendremos una función que cambia su único argumento y lo devuelve. También crearé algunas variables que pasaré a la función una por una. Intente predecir qué se imprimiría en la consola. (La respuesta está en la segunda oración del siguiente párrafo).

    function reassign(arg) { arg = "OMG";}const unreassignable = "What";let reassignable = "is";let non_primitive = { val: "happening" };reassign(unreassignable);reassign(reassignable);reassign(non_primitive);console.log(unreassignable, reassignable, non_primitive.val, " ");

    ¿Tu suposición tiene algún “Dios mío”? No debería haber sido así, ya que la consola mostrará "Qué está pasando ". No importa lo que se pase a una función en JavaScript, la reasignación cambia solo la variable del argumento. Entonces, ni constni letcambie nada aquí porque la función no obtiene la variable en sí. Pero ¿qué pasa si intentamos cambiar las propiedades de un argumento?

    Creé otra función que intenta cambiar la valpropiedad de su argumento. Intenta adivinar el mensaje en la consola esta vez.

     

    function change_val_prop(arg) { try { arg.val = "OMG"; } catch (ignore) {}}const a_string = "What";const a_number = 15;const non_primitive = { val: "happening" };const non_primitive_read_only = Object.freeze({ my_string: "here" });change_val_prop(a_string);change_val_prop(a_number);change_val_prop(non_primitive);change_val_prop(non_primitive_read_only);console.log( a_string.val, a_number.val, non_primitive.val, non_primitive_read_only.val, " ");

    ¿Hay algún “Dios mío” en tu suposición ahora? Genial, el mensaje es "undefinido undefinido Dios mío, undefinido ". La única vez que la función podría cambiar la propiedad es con un objeto común. Qué nos dice esto? ¿Existe alguna diferencia entre cómo se pasan los valores primitivos y cómo son los objetos? ¿Es que el objeto congelado que pasa de repente lo cambia a valor de paso? Creo que es más útil tratarlos como iguales.

    Ahora sobre ese juego de manos que mencioné. Prácticamente todos los recursos hacen eso de decir que las primitivas y los objetos se pasan de manera diferente, y luego inmediatamente lo siguen con un ejemplo en el que los tratan de manera diferente. Mire la descripción de la función en MDN . Al momento de escribir este artículo, lo describió así (el énfasis es mío):

    Los argumentos pueden pasarse por valor (en el caso de valores primitivos) o por referencia (en el caso de objetos). Esto significa que si una función reasigna un parámetro de tipo primitivo, el valor no cambiará fuera de la función. En el caso de un parámetro de tipo de objeto, si sus propiedades están mutadas , el cambio afectará fuera de la función.

    Acabo de mostrarte que la reasignación tampoco cambiaría el objeto. No se pueden cambiar las propiedades de las primitivas porque son de sólo lectura, lo que también ocurre con los objetos congelados. Y la mayoría de los ejemplos que encontrará hacen lo mismo. Primero establecen la diferencia entre dos valores y luego la demuestran utilizando diferentes métodos para cada valor.

    No estoy tratando de criticar, no me malinterpretes. Probablemente se hizo porque explica las peculiaridades de JavaScript de una manera más familiar. Sólo tenga en cuenta que a veces una explicación le proporciona un modelo de pensamiento sobre un problema. Pero el modelo nunca es completamente fiel a la naturaleza de un problema.

    Mirar este tema desde la perspectiva de que los primitivos son como objetos congelados te ayuda a reconocer lo que realmente sucede. Los tutoriales alternativos se vuelven ilógicos. Y ahora, habiendo descubierto esta noción de un objeto primitivo que nadie puede cambiar, hagámosla más amigable para el resto de su programa.

    Mudado

    Los valores primitivos se sostienen por sí solos; cualquier programa sabe cómo manejarlos. Los objetos pueden ser cualquier cosa. Y aunque se les llame primitivos, no basta con que de repente se conviertan en ciudadanos de primera clase. Para lograr algo de eso, necesitamos trabajar un poco.

    Puede definir una forma de convertir objetos en valores primitivos, como cadenas o números. Por ejemplo, creemos un objeto que represente una calificación de cero a cinco. Necesitamos poder trabajar con representación numérica para comparar y clasificar. También necesitamos poder generarlo en texto.

     

    Hay ciertos métodos que puedes definir para describir la representación de tu objeto. Recordar [object Object]? Es lo que obtienes cuando intentas convertir tu objeto en una cadena:

    String({}); // "[object Object]"

    Cambiemos eso.

    Representación de cadenas

    Ese resultado proviene del toStringmétodo predeterminado definido en el prototipo del objeto. Pero puedes sobrescribirlo definiéndolo en tu propio objeto.

    String({ toString: () = "hello there" }); // "hello there"

    Eso es lo que usaremos para nuestros objetos de calificación. Para que sea más conveniente, creemos una función que inicialice y congele dichos objetos. También comprobará si el valor está dentro del rango de cero a cinco y devolverá undefinedlo contrario.

    function new_rating(value) { const max = 5; // That symbol forces textual representation (who needs emoji anyway ). const text_only = "ufe0e"; const star = "⭑" + text_only; const no_star = "⭐" + text_only; if ( !Number.isSafeInteger(value) || (value 0 || value max) ) { return undefined; } return Object.freeze({ value, toString: () = star.repeat(value) + no_star.repeat(max - value) });}

    Ahora califiquemos algo. Hay un bolígrafo que me gusta. Es bastante bueno y le daría cinco estrellas.

    const ratings = new WeakMap();ratings.set(jetstream_pen, new_rating(5));

    Así, WeakMapen el caso de las calificaciones, se pueden asignar propiedades a los objetos sin cambiarlos realmente. Ahora, siempre que queramos tener una calificación, podemos convertir ambos objetos en cadenas.

    if (ratings.has(jetstream_pen)) { console.log(`${jetstream_pen} ${ratings.get(jetstream_pen)}`); // "Uni-Ball Jetstream 0.5 ⭑︎⭑︎⭑︎⭑︎⭑︎"}

    Envolver ambos objetos en una plantilla de cadena literal es en lo que confié aquí para activar el toStringmétodo. De lo contrario, podrías simplemente llamar a la Stringfunción en ellos, como hice yo al principio de esta sección.

    Para los numéricos

    Para los números, existe el valueOfmétodo, que se llama cada vez que se intenta convertir a comparaciones numéricas u operadores matemáticos (excepto +). Agreguémoslo a nuestra new_ratingfunción:

    function new_rating(value) { // ... return Object.freeze({ value, valueOf: () = value, toString: () = star.repeat(value) + no_star.repeat(max - value) });}

    Ahora puede parecer redundante devolver la valuepropiedad directamente. Pero recuerda que nadie más que nosotros sabe que está ahí. Devolverlo valueOfes una forma universal de obtener una representación numérica.

    Digamos que tenemos nuestro objeto bolígrafo nuevamente. Y digamos que la calificación ahora es de su propiedad (solo para simplificar el ejemplo). Ahora podemos filtrar elementos con menos de cuatro estrellas:

    articles.filter((item) = item.rating 3);// [ { name: "Uni-Ball Jetstream 0.5", ... } ]

    De manera similar, podemos ordenar elementos por calificación. Podemos hacerlo usando el sortmétodo de Arrays. Probablemente ya tengas tu pequeña función de clasificación favorita que te gustaría usar, como esta:

    function sorter(first, second) { return second.rating - first.rating;}const sorted_by_rating = array_of.sort(sorter);

    Ahora sorted_by_ratingtiene una variedad de los mejores artículos.

    Conclusión

    Rara vez miraba los objetos como algo que pudiera ampliar lo que se podía expresar en JavaScript. Con objetos primitivos, eso es lo que intento explorar. Todavía hay cosas que no podemos agregar, como nuevos operadores o sintaxis literal, pero aún así, con objetos primitivos, podemos definir nuevos tipos de valores.

    En esta primera parte de la serie Objetos Primitivos , intenté dar una visión general de cómo hacer que los objetos se parezcan a algunas propiedades primitivas. Los congelas para que sean de solo lectura. También puede definir una representación en primitivas, ya sea numérica o de cadena, para que funcione con operadores aritméticos o generarlos en texto.

    En las próximas partes de la semana que viene, mi objetivo es dar más ejemplos de uso y comparación con otros enfoques que he encontrado. Verás cómo facilitar la creación de objetos primitivos y convertirlos en estructuras.

    En esta serie, intento abordar las funciones de JavaScript en las que se puede confiar . Incluso si no todo tiene sentido, espero que al observar algunos de los ejemplos que proporcioné aquí, aprenda algo útil que haga que trabajar con JavaScript sea menos frágil sin recurrir innecesariamente a herramientas adicionales.

    (yk, il)Explora más en

    • javascript
    • Herramientas
    • Técnicas





    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

    Descubriendo objetos primitivos en JavaScript (Parte 1)

    Descubriendo objetos primitivos en JavaScript (Parte 1)

    Patrones de diseño de interfaces inteligentes, vídeo de 10h + formación UX Patrones de diseño de interfaces inteligentes, vídeo de 10h + formación UX

    programar

    es

    https://aprendeprogramando.es/static/images/programar-descubriendo-objetos-primitivos-en-javascript-parte-1-1166-0.jpg

    2024-04-04

     

    Descubriendo objetos primitivos en JavaScript (Parte 1)
    Descubriendo objetos primitivos en JavaScript (Parte 1)

    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