Lo que nos dice la eliminación de propiedades de objetos sobre JavaScript

 

 

 

  • ¡Registro!

  • Índice
    1. Concursante A: "Me puse ca undefined".
    2. Concursante B: "Utilicé el deleteoperador".
    3. Concursante C: "Eliminé la propiedad a través de un Proxyobjeto".
    4. Concursante D: "Evité la mutación mediante el uso de la desestructuración de objetos".
    5. Concursante E: "Usé JSON.stringifyy JSON.parse".
    6. Concursante F: "Confiamos en Lodash en mi empresa".
    7. Conclusión

    Quitar propiedades de un objeto en JavaScript puede no ser el trabajo más apasionante, pero hay muchas maneras de lograrlo, cada una de las cuales revela un aspecto fundamental de cómo funciona JavaScript. Juan Diego Rodríguez explora cada técnica en este artículo.

     

    Se pide a un grupo de concursantes que completen la siguiente tarea:

    Hacer object1similar a object2.

    let object1 = { a: "hello", b: "world", c: "!!!",};let object2 = { a: "hello", b: "world",};

    Parece fácil, ¿verdad? Simplemente elimine la cpropiedad que coincida object2. Sorprendentemente, cada persona describió una solución diferente:

    • Concursante A: "Me puse ca undefined".
    • Concursante B: "Usé el deleteoperador".
    • Concursante C: "Eliminé la propiedad a través de un Proxyobjeto".
    • Concursante D: "Evité la mutación mediante el uso de la desestructuración de objetos".
    • Concursante E: "Usé JSON.stringifyy JSON.parse".
    • Concursante F: "Confiamos en Lodash en mi empresa".

    Se dieron muchísimas respuestas y todas parecen ser opciones válidas. Entonces, ¿quién tiene “razón”? Analicemos cada enfoque.

    Concursante A: "Me puse ca undefined".

    En JavaScript, acceder a una propiedad no existente devuelve undefined.

    const movie = { name: "Up",};console.log(movie.premiere); // undefined

    Es fácil pensar que establecer una propiedad en la undefinedelimina del objeto. Pero si intentamos hacer eso, observaremos un pequeño pero importante detalle:

    const movie = { name: "Up", premiere: 2009,};movie.premiere = undefined;console.log(movie);

    Aquí está el resultado que obtenemos:

    {name: 'up', premiere: undefined}

    Como puede ver, premieretodavía existe dentro del objeto incluso cuando es undefined. En realidad, este enfoque no elimina la propiedad sino que cambia su valor. Podemos confirmar que usando el hasOwnProperty()método:

    const propertyExists = movie.hasOwnProperty("premiere");console.log(propertyExists); // true

    Pero entonces, ¿por qué, en nuestro primer ejemplo, el acceso object.premiereregresa undefinedsi la propiedad no existe en el objeto? ¿No debería arrojar un error como al acceder a una variable inexistente?

    console.log(iDontExist);// Uncaught ReferenceError: iDontExist is not defined

    La respuesta está en cómo ReferenceErrorse comporta y qué referencia es, en primer lugar.

    Una referencia es un enlace de nombre resuelto que indica dónde se almacena un valor. Consta de tres componentes: un valor base , el nombre al que se hace referencia y un indicador de referencia estricto .

    Para una user.namereferencia, el valor base es el objeto, usermientras que el nombre al que se hace referencia es la cadena, namey el indicador de referencia estricta es falsesi el código no está en strict mode.

    Las variables se comportan de manera diferente. No tienen un objeto principal, por lo que su valor base es un registro de entorno , es decir, un valor base único asignado cada vez que se ejecuta el código.

    Si intentamos acceder a algo que no tiene un valor base, JavaScript arrojará un archivo ReferenceError. Sin embargo, si se encuentra un valor base, pero el nombre al que se hace referencia no apunta a un valor existente, JavaScript simplemente asignará el valor undefined.

    “El tipo Indefinido tiene exactamente un valor, llamado indefinido . Cualquier variable a la que no se le haya asignado un valor tiene el valor indefinido ”.

     

    — Especificación ECMAScript

    ¡ Podríamos dedicar un artículo entero a abordar undefinedlas travesuras!

    Concursante B: "Utilicé el deleteoperador".

    El deleteúnico propósito del operador es eliminar una propiedad de un objeto y regresar truesi el elemento se elimina con éxito.

    const dog = { breed: "bulldog", fur: "white",};delete dog.fur;console.log(dog); // {breed: 'bulldog'}

    Algunas advertencias vienen con el deleteoperador que debemos tener en cuenta antes de usarlo. Primero, el deleteoperador se puede utilizar para eliminar un elemento de una matriz. Sin embargo, deja una ranura vacía dentro de la matriz, lo que puede causar un comportamiento inesperado ya que propiedades como lengthno se actualizan y aún cuentan la ranura abierta.

    const movies = ["Interstellar", "Top Gun", "The Martian", "Speed"];delete movies[2];console.log(movies); // ['Interstellar', 'Top Gun', empty, 'Speed']console.log(movies.length); // 4

    En segundo lugar, imaginemos el siguiente objeto anidado:

    const user = { name: "John", birthday: {day: 14, month: 2},};

    Intentar eliminar la birthdaypropiedad usando el deleteoperador funcionará bien, pero existe la idea errónea de que al hacerlo se libera la memoria asignada para el objeto.

    En el ejemplo anterior, birthdayes una propiedad que contiene un objeto anidado. Los objetos en JavaScript se comportan de manera diferente a los valores primitivos (por ejemplo, números, cadenas y valores booleanos) en cuanto a cómo se almacenan en la memoria. Se almacenan y copian "por referencia", mientras que los valores primitivos se copian de forma independiente como un valor completo.

    Tomemos, por ejemplo, un valor primitivo como una cadena:

    let movie = "Home Alone";let bestSeller = movie;

    En este caso, cada variable tiene un espacio independiente en la memoria. Podemos ver este comportamiento si intentamos reasignar uno de ellos:

    movie = "Terminator";console.log(movie); // "Terminator"console.log(bestSeller); // "Home Alone"

    En este caso la reasignación movieno afecta bestSellerya que están en dos espacios diferentes en la memoria. Las propiedades o variables que contienen objetos (por ejemplo, objetos normales, matrices y funciones) son referencias que apuntan a un único espacio en la memoria. Si intentamos copiar un objeto, simplemente estamos duplicando su referencia.

    let movie = {title: "Home Alone"};let bestSeller = movie;bestSeller.title = "Terminator";console.log(movie); // {title: "Terminator"}console.log(bestSeller); // {title: "Terminator"}

    Como puede ver, ahora son objetos y reasignar una bestSellerpropiedad también cambia el movieresultado. Debajo del capó, JavaScript mira el objeto real en la memoria y realiza el cambio, y ambas referencias apuntan al objeto modificado.

     

    Al saber cómo se comportan los objetos "por referencia", ahora podemos entender cómo el uso del deleteoperador no libera espacio en la memoria.

    El proceso por el cual los lenguajes de programación liberan memoria se llama recolección de basura . En JavaScript, la memoria se libera para un objeto cuando no hay más referencias y se vuelve inalcanzable . Por lo tanto, el uso del deleteoperador puede hacer que el espacio de la propiedad sea elegible para la recopilación, pero puede haber más referencias que impidan que se elimine de la memoria.

    Ya que estamos en el tema, vale la pena señalar que existe un cierto debate en torno al deleteimpacto del operador en el rendimiento . Puedes seguir el rastro del conejo desde el enlace, pero seguiré adelante y te estropearé el final: la diferencia en el rendimiento es tan insignificante que no plantearía un problema en la gran mayoría de los casos de uso. Personalmente, considero que el enfoque idiomático y directo del operador es una victoria sobre un minúsculo impacto en el rendimiento.

    Dicho esto, se puede argumentar en contra del uso deleteya que muta un objeto. En general, es una buena práctica evitar las mutaciones, ya que pueden provocar un comportamiento inesperado en el que una variable no tiene el valor que asumimos que tiene.

    Concursante C: "Eliminé la propiedad a través de un Proxyobjeto".

    Este concursante definitivamente fue un fanfarrón y usó un sustituto para su respuesta. Un proxy es una forma de insertar una lógica intermedia entre las operaciones comunes de un objeto, como obtener, configurar, definir y, sí, eliminar propiedades. Funciona a través del Proxyconstructor que toma dos parámetros:

    • target: El objeto desde donde queremos crear un proxy.
    • handler: Un objeto que contiene la lógica intermedia para las operaciones.

    Dentro de handler, definimos métodos para las diferentes operaciones, llamados trampas , porque interceptan la operación original y realizan un cambio personalizado. El constructor devolverá un Proxyobjeto, un objeto idéntico a target, pero con la lógica intermedia agregada.

    const cat = { breed: "siamese", age: 3,};const handler = { get(target, property) { return `cat's ${property} is ${target[property]}`; },};const catProxy = new Proxy(cat, handler);console.log(catProxy.breed); // cat's breed is siameseconsole.log(catProxy.age); // cat's age is 3

    Aquí, handlermodifica la operación de obtención para devolver un valor personalizado.Te recomendamos Aprender a programar con ejemplos

    Digamos que queremos registrar la propiedad que estamos eliminando en la consola cada vez que usamos el deleteoperador. Podemos agregar esta lógica personalizada a través de un proxy usando la deletePropertytrampa.

     

    const product = { name: "vase", price: 10,};const handler = { deleteProperty(target, property) { console.log(`Deleting property: ${property}`); },};const productProxy = new Proxy(product, handler);delete productProxy.name; // Deleting property: name

    El nombre de la propiedad se registra en la consola pero arroja un error en el proceso:

    Uncaught TypeError: 'deleteProperty' on proxy: trap returned falsish for property 'name'

    El error se produce porque el controlador no tenía un returnvalor. Eso significa que por defecto es undefined. En modo estricto, si el deleteoperador devuelve false, generará un error y undefined, al ser un valor falso, desencadena este comportamiento.

    Si intentamos regresar truepara evitar el error, encontraremos un tipo diferente de problema:

    // ...const handler = { deleteProperty(target, property) { console.log(`Deleting property: ${property}`); return true; },};const productProxy = new Proxy(product, handler);delete productProxy.name; // Deleting property: nameconsole.log(productProxy); // {name: 'vase', price: 10}

    ¡La propiedad no se elimina!

    Reemplazamos el deletecomportamiento predeterminado del operador con este código, por lo que no recuerda que tiene que "eliminar" la propiedad.

    Aquí es donde Reflectentra en juego.

    Reflectes un objeto global con una colección de todos los métodos internos de un objeto. Sus métodos se pueden usar como operaciones normales en cualquier lugar, pero están destinados a usarse dentro de un proxy.

    Por ejemplo, podemos resolver el problema en nuestro código devolviendo Reflect.deleteProperty()(es decir, la Reflectversión del deleteoperador) dentro del controlador.

    const product = { name: "vase", price: 10,};const handler = { deleteProperty(target, property) { console.log(`Deleting property: ${property}`); return Reflect.deleteProperty(target, property); },};const productProxy = new Proxy(product, handler);delete productProxy.name; // Deleting property: nameconsole.log(product); // {price: 10}

    Vale la pena señalar que ciertos objetos, como Math, Datey JSON, tienen propiedades que no se pueden eliminar mediante el deleteoperador ni ningún otro método. Estas son propiedades de objetos "no configurables", lo que significa que no se pueden reasignar ni eliminar. Si intentamos utilizar el deleteoperador en una propiedad no configurable, fallará silenciosamente y devolverá falseo arrojará un error si ejecutamos nuestro código en modo estricto.

    "use strict";delete Math.PI;

    Producción:

    Uncaught TypeError: Cannot delete property 'PI' of #Object

    Si queremos evitar errores con el deleteoperador y las propiedades no configurables, podemos usar el Reflect.deleteProperty()método ya que no arroja un error al intentar eliminar una propiedad no configurable, incluso en modo estricto, porque falla silenciosamente.

    Supongo, sin embargo, que preferiría saber cuándo está intentando eliminar un objeto global en lugar de evitar el error.

     

    Concursante D: "Evité la mutación mediante el uso de la desestructuración de objetos".

    La desestructuración de objetos es una sintaxis de asignación que extrae las propiedades de un objeto en variables individuales. Utiliza una notación de llaves ( {}) en el lado izquierdo de una tarea para indicar cuál de las propiedades obtener.

    const movie = { title: "Avatar", genre: "science fiction",};const {title, genre} = movie;console.log(title); // Avatarconsole.log(genre); // science fiction

    También funciona con matrices usando corchetes ( []):

    const animals = ["dog", "cat", "snake", "elephant"];const [a, b] = animals;console.log(a); // dogconsole.log(b); // cat

    La sintaxis extendida ( ...) es algo así como la operación opuesta porque encapsula varias propiedades en un objeto o una matriz si son valores únicos.

    Podemos usar la desestructuración de objetos para descomprimir los valores de nuestro objeto y la sintaxis extendida para conservar solo los que queremos:

    const car = { type: "truck", color: "black", doors: 4};const {color, ...newCar} = car;console.log(newCar); // {type: 'truck', doors: 4}

    ¡De esta manera, evitamos tener que mutar nuestros objetos y los posibles efectos secundarios que conlleva!

    Aquí hay un caso extremo con este enfoque: eliminar una propiedad solo cuando es undefined. Gracias a la flexibilidad de la desestructuración de objetos, podemos eliminar propiedades cuando lo son undefined(o false , para ser exactos).

    Imagine que tiene una tienda en línea con una amplia base de datos de productos. Tienes una función para encontrarlos. Por supuesto, necesitará algunos parámetros, tal vez el nombre del producto y la categoría.

    const find = (product, category) = { const options = { limit: 10, product, category, }; console.log(options); // Find in database...};

    En este ejemplo, el producto namedebe ser proporcionado por el usuario para realizar la consulta, pero categoryes opcional. Entonces, podríamos llamar a la función así:

    find("bedsheets");

    Y como categoryno se especifica a, devuelve como undefined, lo que da como resultado el siguiente resultado:

    {limit: 10, product: 'beds', category: undefined}

    En este caso, no deberíamos usar parámetros predeterminados porque no buscamos una categoría específica.

    Observe cómo la base de datos podría asumir incorrectamente que estamos consultando productos en una categoría llamada undefined! Esto conduciría a un resultado vacío, que es un efecto secundario no deseado. Aunque muchas bases de datos filtrarán la undefinedpropiedad por nosotros, sería mejor desinfectar las opciones antes de realizar la consulta. Una forma interesante de eliminar dinámicamente una undefinedpropiedad es mediante la destrucción de objetos junto con el ANDoperador ( ).

    En lugar de escribir optionsasí:

     

    const options = { limit: 10, product, category,};

    …podemos hacer esto en su lugar:

    const options = { limit: 10, product, ...(category {category}),};

    Puede parecer una expresión compleja, pero después de comprender cada parte, se convierte en una frase sencilla. Lo que estamos haciendo es aprovecharnos del operador.

    El ANDoperador se usa principalmente en declaraciones condicionales para decir:

    Si Ay Bson true, entonces haz esto.

    Pero en esencia, evalúa dos expresiones de izquierda a derecha, devolviendo la expresión de la izquierda si es falsa y la expresión de la derecha si ambas son verdaderas . Entonces, en nuestro ejemplo anterior, el ANDoperador tiene dos casos:

    1. categoryes undefined(o falso );
    2. categoryse define.

    En el primer caso en el que es falso , el operador devuelve la expresión de la izquierda category. Si conectamos categoryel resto del objeto, se evalúa de esta manera:

    const options = { limit: 10, product, ...category,};

    Y si intentamos desestructurar cualquier valor falso dentro de un objeto, se desestructurará hasta convertirlo en nada:

    const options = { limit: 10, product,};

    En el segundo caso, dado que el operador es veraz , devuelve la expresión de la derecha {category}. Cuando se conecta al objeto, se evalúa de esta manera:

    const options = { limit: 10, product, ...{category},};

    Y como categoryestá definido, se desestructura en una propiedad normal:

    const options = { limit: 10, product, category,};

    Poniéndolo todo junto, obtenemos la siguiente betterFind()función:

    const betterFind = (product, category) = { const options = { limit: 10, product, ...(category {category}), }; console.log(options); // Find in a database...};betterFind("sofas");

    Y si no especificamos ninguno category, simplemente no aparece en el optionsobjeto final.

    {limit: 10, product: 'sofas'}

    Concursante E: "Usé JSON.stringifyy JSON.parse".

    Sorprendentemente para mí, hay una manera de eliminar una propiedad reasignándola a undefined. El siguiente código hace exactamente eso:

    let monitor = { size: 24, screen: "OLED",};monitor.screen = undefined;monitor = JSON.parse(JSON.stringify(monitor));console.log(monitor); // {size: 24}

    En cierto modo te mentí ya que estamos empleando algunas travesuras de JSON para lograr este truco, pero podemos aprender algo útil e interesante de ellas.

    Aunque JSON se inspira directamente en JavaScript, se diferencia en que tiene una sintaxis fuertemente tipada. No permite funciones ni undefinedvalores, por lo que su uso JSON.stringify()omitirá todos los valores no válidos durante la conversión, lo que dará como resultado texto JSON sin las undefinedpropiedades. A partir de ahí, podemos analizar el texto JSON en un objeto JavaScript usando el JSON.parse()método.

    Es importante conocer las limitaciones de este enfoque. Por ejemplo, JSON.stringify()omite funciones y genera un error si se encuentra una referencia circular (es decir, una propiedad hace referencia a su objeto principal) o un BigIntvalor.

    Concursante F: "Confiamos en Lodash en mi empresa".

    Vale la pena señalar que las bibliotecas de utilidades como Lodash.js, Underscore.js o Ramda también proporcionan métodos para eliminar (o pick()) propiedades de un objeto. No analizaremos diferentes ejemplos para cada biblioteca ya que su documentación ya hace un excelente trabajo al respecto.

    Conclusión

    Volviendo a nuestro escenario inicial, ¿qué concursante tiene razón?

    La respuesta: ¡ Todos! Bueno, excepto el primer concursante. Establecer una propiedad en undefinedjust no es un enfoque que queramos considerar para eliminar una propiedad de un objeto, dadas todas las otras formas que tenemos de hacerlo.

    Como ocurre con la mayoría de las cosas en desarrollo, el enfoque más "correcto" depende de la situación. Pero lo interesante es que detrás de cada enfoque hay una lección sobre la naturaleza misma de JavaScript. Comprender todas las formas de eliminar una propiedad en JavaScript puede enseñarnos aspectos fundamentales de la programación y JavaScript, como la administración de memoria, la recolección de basura, los servidores proxy, JSON y la mutación de objetos. ¡Eso es mucho aprendizaje para algo aparentemente tan aburrido y trivial!

    Lecturas adicionales sobre SmashingMag

    • “ Descubrimiento de objetos primitivos en JavaScript (Parte 1) ”, Kirill Myshkin
    • “ Objetos primitivos en JavaScript: cuándo usarlos (Parte 2) ”, Kirill Myshkin
    • “ Una reintroducción a la tarea desestructurante ”, Laurie Barth
    • “ Geometría del modelo de objetos de documento (DOM): introducción y guía para principiantes ”, Pearl Akpan

    (gg, yk)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

    Lo que nos dice la eliminación de propiedades de objetos sobre JavaScript

    Concursante A: 'Me puse ca undefined'.Concursante B: 'Utilicé el deleteoperador'.Concursante C: 'Eliminé la propiedad a través de un Proxyobjeto'.Concursant

    programar

    es

    2025-01-08

     

    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

     

     

    Update cookies preferences