¿Qué son bind(), call() y apply()? ¿Cómo trabajan?

Para responder a esta pregunta, es importante recordar primero que, en JavaScript, todas las funciones son objetos. Esto significa que pueden tener propiedades y métodos, como cualquier otro objeto. Las funciones son un tipo especial de objeto ya que vienen con una variedad de propiedades integradas (que tienen una propiedad de código que es invocable, que tienen un nombre opcional y los tres métodos call(), apply() y bind()) .

Las funciones son un tipo especial de objeto ya que vienen con una variedad de propiedades y métodos integrados, tres de los cuales son call(), apply() y bind().

.bind()

Creemos un objeto persona con dos propiedades y un método. El método será una expresión de función getNombreCompleto() que usa la palabra clave ‘this’.

Nota al margen importante: en un método (una propiedad de función en un objeto), la palabra clave ‘this’ en realidad apunta al objeto en el que está contenido, NO a la función en sí (esta es una parte extraña y a veces frustrante de JavaScript que puede resultar en bugs difíciles de ver).

var persona = {
  primerNombre: 'Blaise',
  segundoNombre: 'Pascal',
  getNombreCompleto: function() {

    var nombreCompleto = this.primerNombre + ' ' + this.segundoNombre
    return nombreCompleto
  }
}

Ahora creemos una función logNombre() que exista fuera de nuestro objeto persona.

Si llamamos logNombre() ahora mismo, obtenemos un error. Esto se debe a que logNombre() está definido en el ámbito global, por lo que ‘this’ apunta al objeto global, que no tiene un método getNombreCompleto().

var logNombre = function() {
  console.log('INFO: ' + this.getNombreCompleto())
}

logNombre()

¿No sería bueno si pudiéramos controlar a qué apunta la palabra clave “esto”?


Introduzca bind().

Creemos una nueva función llamada logNombrePersona() que use la propiedad incorporada .bind() en nuestro objeto de función logNombre. Cualquier objeto que pasemos como argumento a .bind() se convierte en lo que apunta la variable ‘this’ cuando se ejecuta la función.

var logNombre = function() {
  console.log('INFO: ' + this.getNombreCompleto())
}

var logNombrePersona = logNombre.bind(persona)
logNombrePersona()

Ahora nuestra variable “this” apunta al objeto persona, accede a sus propiedades y funciona exactamente como se esperaba.

Tenga en cuenta que no estamos invocando la función antes de llamar a .bind(). Recuerda lo que dijimos al principio. Las funciones son objetos, lo que significa que no es necesario invocarlos inmediatamente. Estamos usando la función como un objeto y accediendo a su propiedad incorporada bind(). Por eso estamos escribiendo logNombre.bind() y no logName(). bind(). Lo último sería invocar la función e intentar acceder a un método .bind() en el valor de retorno, que obviamente no tiene.

Cualquier objeto que pasemos como argumento a .bind() se convierte en lo que apunta la variable “this” cuando se ejecuta la función.

La función bind() devuelve una nueva función. Hace una copia de logNombre() y le dice al motor JavaScript: “Siempre que se invoque esta copia de logNombre(), establezca su palabra clave ‘this’ para hacer referencia a la persona durante su contexto de ejecución”. Este comportamiento de devolver una nueva función / hacer una copia es diferente de call() y apply(), a lo que llegaremos momentáneamente.

Ahora que tenemos una copia de nuestra función logNombre() que hace referencia al objeto person (logNombrePersona()), utilicemos sus argumentos.

var persona = {
  primerNombre: 'Blaise',
  segundoNombre: 'Pascal',
  getNombreCompleto: function() {
      var nombreCompleto = this.primerNombre + ' ' + this.segundoNombre
      return nombreCompleto

  }
}

var logNombre = function(lang1, lang2) {
  console.log('INFO: ' + this.getNombreCompleto())
  console.log('ARGS: ' + lang1 + ' ' + lang2)
}

var logNombrePersona = logNombre.bind(persona)
logNombrePersona('en', 'es')

.call()

call() es similar a bind() en el sentido de que le permite pasar lo que le gustaría que señalara un valor “this”. Excepto que la call() no hace una copia. Invoca la función de inmediato. Permitiéndole pasar la referencia “esto” y cualquier argumento mientras llama a la función simultáneamente.

Intentemos llamar a logNombre() usando call(). Le haremos saber que hace referencia a la persona como su variable ‘this’ y le daremos dos idiomas como argumentos.

var persona = {
  primerNombre: 'Blaise',
  segundoNombre: 'Pascal',
  getNombreCompleto: function() {
      var nombreCompleto = this.primerNombre + ' ' + this.segundoNombre
      return nombreCompleto

  }
}

var logNombre = function(lang1, lang2) {
  console.log('INFO: ' + this.getNombreCompleto())
  console.log('ARGS: ' + lang1 + ' ' + lang2)
}

logNombre.call(persona, 'en', 'es')

¿Parece similar? Si bien es similar, no es lo mismo en absoluto. Obtenemos el mismo resultado que con bind(), excepto que esta vez no hicimos una copia y llamamos a la función inmediatamente.

.apply()

Este es fácil. Hace exactamente lo mismo que call(). Excepto por una diferencia.


Apply() hace exactamente lo mismo que call(), pero requiere que los argumentos de la función original se pasen como un arreglo.

logNombre.apply(persona, 'en', 'es')
//Uncaught TypeError: CreateListFromArrayLike called on non-object

logNombre.apply(persona, ['en', 'es'])

En resumen

  • Bind(): Devuelve una copia de una función y toma un argumento de lo que le gustaría que apunte la variable “this” dentro de la función copiada. Puede tomar argumentos adicionales que actúen como parámetros preestablecidos permanentes para su copia de la función original.
  • Call(): es similar a bind() en que acepta la referencia de la variable “this” como argumento, pero llama a la función inmediatamente y no crea una copia.
  • Apply(): funciona casi exactamente igual que call (), excepto que los argumentos de la función original deben pasarse como una matriz (esto no incluye la referencia “this”, que está fuera de la matriz).

Categorized in:

Tagged in:

, , ,