DMarlon Studio

Lección 2 · 2 de 20

this, bind, call y apply · JavaScript

Entienda a qué apunta this en JavaScript y cómo fijarlo con bind, call y apply, con un carrito de la compra como ejemplo.

JavaScript · this, bind, call y apply en DMarlon Studio: el código del ejemplo y su salida en la terminal

Dentro de un método, `this` es el objeto que va delante del punto en la llamada. Por eso cart.describe() puede leer this.owner y llamar a this.total(): en esa llamada, this es cart. El valor de this no depende de dónde se escribió la función, sino de cómo se llama.

Si copia el método en una variable (const loose = cart.describe) y lo llama solo, ya no hay objeto delante del punto: this deja de ser el carrito y this.total no existe, de ahí el error this.total is not a function. Es el fallo típico al pasar un método como callback, por ejemplo a setTimeout o al clic de un botón.

`bind` lo arregla: cart.describe.bind(cart) devuelve una función nueva cuyo this queda fijado para siempre. `call` y `apply`, en cambio, ejecutan la función en ese momento con el this que usted elija: describe.call(otherCart) describe el carrito de Luis con el mismo código. Solo se diferencian en los argumentos: call los recibe uno a uno y apply, en un array.

Las funciones flecha no tienen this propio: usan el de la función que las rodea. En labels(), la flecha de map ve el mismo this que el método, y por eso cada etiqueta lleva Ana. En DMarlon Studio, JavaScript es el lenguaje de los destinos Electron, React, Next.js y React Native, y este detalle explica por qué se usan tantas flechas en los manejadores de eventos.

Código · main.js

const cart = {  owner: "Ana",  items: [12.5, 30, 7.25],  total() {    return this.items.reduce((sum, price) => sum + price, 0);  },  describe() {    return `${this.owner} owes ${this.total().toFixed(2)} EUR`;  },  // An arrow function has no "this" of its own: it uses the method's one.  labels() {    return this.items.map((price) => `${this.owner}:${price}`);  },};console.log(cart.describe());console.log(cart.labels().join(" "));// Losing "this": the method is called without its object.const loose = cart.describe;try { loose(); } catch (err) { console.log("Lost this:", err.message); }// bind returns a new function with "this" fixed for good.const bound = cart.describe.bind(cart);console.log("bind:", bound());// call and apply run the function now with the "this" you choose.const otherCart = { owner: "Luis", items: [5, 5], total: cart.total };console.log("call:", cart.describe.call(otherCart));function withTax(rate, label) {  return `${label}: ${(this.total() * (1 + rate)).toFixed(2)} EUR`;}console.log(withTax.call(cart, 0.21, "Ana with VAT"));console.log(withTax.apply(otherCart, [0.1, "Luis with 10%"]));

Salida · node main.js

Ana owes 49.75 EUR
Ana:12.5 Ana:30 Ana:7.25
Lost this: this.total is not a function
bind: Ana owes 49.75 EUR
call: Luis owes 10.00 EUR
Ana with VAT: 60.20 EUR
Luis with 10%: 11.00 EUR

Para recordar

  • this lo decide la forma de llamar, no el lugar donde se escribe la función.
  • Un método separado de su objeto pierde su this.
  • bind devuelve una función con this fijo; call y apply la ejecutan en el acto.
  • call recibe los argumentos sueltos; apply, en un array.
  • Las flechas toman el this de la función que las rodea.

Ejercicio

Cree un carrito para Marta con owner e items pero sin métodos y calcule su importe con cart.total.call(...). Después cambie la flecha de labels() por una function normal y observe qué ocurre con this.owner.