Explorando la palabra clave super y el operador spread en objetos de JavaScript

La palabra clave super

En JavaScript, mientras que this hace referencia al objeto actual donde se ejecuta una función, ES6 introdujo super, el cual apunta directamente al prototipo del objeto actual.

const baseEntity = {
  status: 'active',
  getInfo() {
    return this.status;
  }
};

const userEntity = {
  status: 'inactive',
  fetchBaseStatus() {
    return super.status;
  }
};

Object.setPrototypeOf(userEntity, baseEntity);
console.log(userEntity.fetchBaseStatus()); // 'active'

En el ejemplo anterior, el método fetchBaseStatus utiliza super.status para acceder a la propiedad status definida en baseEntity.

Es crucial entender que super solo puede utiilzarse dentro de la sintaxis abreviada de métodos de un objeto. Intentar usarlo en otros contextos generará errores de sintaxis o ejecución.

// Error: asignación directa a propiedad
const invalidObj1 = {
  val: super.status
};

// Error: función flecha
const invalidObj2 = {
  val: () => super.status
};

// Error: expresión de función
const invalidObj3 = {
  val: function() {
    return super.status;
  }
};

Estos ejemplos fallan porque el motor de JavaScript no los reconoce como métodos abreviados de objeto. Solo la sintaxis de método abreviado permite que el motor identifique correctamente el contexto de super.

Internamente, cuando se accede a una propiedad mediante super, el motor lo traduce a Object.getPrototypeOf(this).propiedad. Si se invoca un método, se ejecuta como Object.getPrototypeOf(this).metodo.call(this).

const parent = {
  role: 'admin',
  displayRole() {
    console.log(this.role);
  }
};

const child = {
  role: 'guest',
  displayRole() {
    super.displayRole();
  }
};

Object.setPrototypeOf(child, parent);
child.displayRole(); // Imprime 'guest'

Aunque super.displayRole() invoca el método del prototipo parent, el enlace de this dentro de ese método sigue apuntando al objeto original child. Por ello, se imprime 'guest'.

El operador spread en objetos y desestructuración

ES2018 extendió el operador de propagación (...) para trabajar con objetos, permitiendo operaciones de desestructuración y clonación muy similares a las que ya existían para arrays.

Asignación por desestructuración (Rest properties)

Al desestructurar un objeto, el operador de reposo (...) captura todas las propiedades enumerables restantes que no han sido extraídas explícitamente, copiándolas en un nuevo objeto.

const { id, name, ...metadata } = { id: 101, name: 'Alice', role: 'editor', level: 5 };
console.log(id); // 101
console.log(name); // 'Alice'
console.log(metadata); // { role: 'editor', level: 5 }

La variable metadata absorbe las claves role y level junto con sus valores.

El lado derecho de la asignación debe ser coercible a un objeto. Si es null o undefined, se lanzará una excepción.

let { ...rest } = null; // Error en tiempo de ejecución
let { ...rest } = undefined; // Error en tiempo de ejecución

Además, la propiedad de reposo debe ser estrictamente el último elemento en la desestructuración.

let { ...rest, id } = someData; // Error de sintaxis
let { id, ...rest1, ...rest2 } = someData; // Error de sintaxis

Es importante destacar que esta operación realiza una copia superficial (shallow copy). Si una propiedad contiene un tipo de dato compuesto (como otro objeto o un array), se copiará la referencia en memoria, no el valor en sí.

const source = { config: { theme: 'dark' } };
const { ...copy } = source;
source.config.theme = 'light';
console.log(copy.config.theme); // 'light'

Al modificar source.config, el cambio se refleja en copy.config porque ambos apuntan al mismo objeto anidado.

Una aplicación práctica de la desestructuración con reposo es la extensión de parámetros en funciones, permitiendo separar argumentos específicos de un conjunto de configuración.

function coreProcess({ host, port }) {
  // Lógica principal de conexión
}

function extendedProcess({ timeout, retries, ...coreParams }) {
  // Lógica adicional usando timeout y retries
  return coreProcess(coreParams);
}

Propagación de propiedades (Spread properties)

El operador ... también se utiliza para expandir las propiedades de un objeto origen dentro de un nuevo objeto destino.

const defaults = { theme: 'light', lang: 'en' };
const custom = { ...defaults };
console.log(custom); // { theme: 'light', lang: 'en' }

Dado que los arrays son objetos en JavaScript, el operador spread puede convertir un array en un objeto donde los índices se convietren en claves.

const colors = ['red', 'green', 'blue'];
const colorObj = { ...colors };
console.log(colorObj); // { 0: 'red', 1: 'green', 2: 'blue' }

Si se propaga un objeto vacío, no se añade ninguna propiedad.

const merged = { ...{}, extra: true };
console.log(merged); // { extra: true }

Finalmente, si el operando no es un objeto, el motor lo envolverá automáticamente en su objeto correspondiente. Si este objeto envoltorio no tiene propiedades propias enumerables, el resultado será un objeto vacío.

const numWrap = { ...42 };
console.log(numWrap); // {}

En este caso, 42 se convierte en new Number(42), el cual no posee propiedades enumerables propias, resultando en un objeto sin claves.

Etiquetas: JavaScript ES6 es2018 object-spread super-keyword

Publicado el 9-16 16:29