¿Qué es this?
Tags: javascript
Published: 2021-08-29 17:25:36

Seguramente no es la primera vez que ves esta pregunta. Quizás te la han hecho en alguna entrevista de trabajo o en un curso de JavaScript.


Lo cierto es que pocos saben lo que realmente es _this_ en JavaScript. La razón más común es por el concepto que tenemos de esa palabra en lenguajes como C#, y que nos hace pensar erróneamente que debe referirse a lo mismo.


Normalmente hasta que no nos topamos con un funcionamiento “extraño” en nuestro código, no nos detenemos a pensar en si sabemos lo que realmente es. De hecho lo primero que hacemos es echarle la culpa a JavaScript y no a nuestra falta de interés por saber lo que está pasando.


Si te sentiste identificado o simplemente quieres saber de un modo sencillo lo que es _this_, sigue leyendo.


## ¿Qué NO es this? ##

**NO es una referencia a sí mismo.** Esta quizás es la respuesta más común que escuchamos, decir que _this_ es una referencia a la función en la que se encuentra.


**NO es una referencia al _lexical scope_ de la función.** Tampoco es una referencia al _scope_ que definimos al momento de escribir nuestro código. (Más sobre _lexical scope_ en un próximo post).


##...entonces ¿Qué es this?##

Es un binding que se hace cuando una función es invocada, y a lo que realmente hace referencia this dependerá completamente del **lugar en dónde se ha llamado** (call-site) dicha función, no el **lugar en donde se ha declarado.**


Pero, ¿cómo funciona? si es un _binding_, ¿se puede cambiar? Veamos ahora qué **4** reglas se aplican para determinar que es _this_.


###Default Binding###

Esta es la que se aplica cuando ninguna otra regla pueda hacerlo. El siguiente ejemplo lo ilustra:


    function foo() {
        console.log(this.a);
    }
    
    var a = 2;
    
    foo(); // 2
        

En este caso **this.a** hace referencia a la variable global a que hemos declarado. Como la llamada a la función **foo()** es una llamada común y ninguna de las otras reglas que veremos más adelante es aplicable, la regla por defecto es la ganadora.


###Implicit Binding###

La siguiente regla aplicaría cuando el lugar en donde se hace la llamada tiene un objeto como contexto, por ejemplo:


    function foo() {
        console.log(this.a);
    }
    
    var obj = {
        a: 2,
        foo: foo
    };
    
    obj.foo(); // 2
        

Como **obj** es _this_ para **foo(), this.a** sería sinónimo de **obj.a**

Cabe destacar que solo el nivel más alto de la cadena de referencias a una propiedad es la que se tomaría en cuenta, es decir:


    function foo() {
        console.log(this.a);
    }
    
    var obj2 = {
        a: 42,
        foo: foo
    };
    
    var obj1 = {
        a: 2,
        obj2: obj2
    };
    
    obj1.obj2.foo(); // 42
        

Una de las cosas que pueden frustrarnos a veces es la pérdida de este _binding_ implícito, lo que usualmente significa que se aplicaría la regla por defecto y _this_ acabaría siendo el global _object o undefined_, depende de si usamos _strict mode_ o no, por ejemplo:


    function foo() {
        console.log(this.a);
    }
    
    var obj = {
        a: 2,
        foo: foo
    };
    
    var bar = obj.foo; // alias con referencia a la función
    
    var a = 'global!!'; // propiedad 'a' en objeto global
    
    bar(); // 'global!!'
        

Puede parecer que **bar** es una referencia a **obj.foo** pero en realidad es una referencia a **foo** en sí mismo, además recordemos que lo que importa es el lugar en donde se llama y **bar()** es una llamada común sin decoración por lo que la regla por defecto aplicaría.


Otro escenario muy común donde ocurre esto es cuando pasamos una función como **callback**:


    function foo() {
        console.log(this.a);
    }
    
    function doFoo(fn) {
        // 'fn' otra referencia a foo
    
        fn(); // <-- call-site
    }
    
    var obj = {
        a: 2,
        foo: foo
    };
    
    var a = 'global!!'; // propiedad 'a' en objeto global
    
    doFoo(obj.foo); // 'global!!'
        

Lo mismo ocurre si usamos una función _built-in_ en JavaScript:


    function foo() {
        console.log(this.a);
    }
    
    var obj = {
        a: 2,
        foo: foo
    };
    
    var a = 'global!!'; // propiedad 'a' en objeto global
    
    setTimeout(obj.foo, 100); // 'global!!'
        

Veamos ahora cómo “solucionar” este problema con la siguiente regla.


###Explicit Binding###

En la regla anterior creamos una propiedad en un objeto y le asignamos una referencia a una función para que se enlazara _this_ al objeto de manera indirecta.


Pero, y si queremos forzar a una función a que use un objeto en particular para _this_, sin tener que crear una propiedad con la referencia a la función dentro del objeto en cuestión?

¿Te suenan los métodos **call o apply**?


Todas las funciones tienen estos métodos disponibles, a través de su **[[Prototype]]**, los cuales son muy útiles para lograr este cometido.


¿Cómo funcionan? Ambos reciben, como primer parámetro, el objeto que deben usar como _this_, y luego invocan la función. Cómo estamos indicando directamente lo que queremos que sea _this_, lo llamamos _binding_ explícito.


    function foo() {
        console.log(this.a);
    }
    
    var obj = {
        a: 2
    };
    
    foo.call(obj); // 2
        

Desafortunadamente, el _explicit binding_ sigue sin ofrecernos una solución al problema planteado anteriormente.


Veamos una variación de este patrón que sí puede darnos una salida y que se introdujo como una utilidad _built-in_ en JavaScript a partir de ES5.


Estamos hablando de **Function.prototype.bind**, y se usa de la siguiente manera:


###Hard Binding###

    function foo(something) {
        console.log(this.a, something);
        return this.a + something;
    }
    
    var obj = {
        a: 2
    };
    
    var bar = foo.bind(obj);
    
    var b = bar(3); // 2 3
    console.log(b); // 5
        

**bind(...)** retorna una nueva función que está forzada a llamar a la función original usando el contexto de _this_ que le hemos especificado.


###new Binding###

La cuarta y última regla se aplica cuando una función es invocada usando **new (constructor call).**


Esto es lo que pasa cuando usamos **new** frente a una función:

Un objeto nuevo se crea de la nada.

El objeto nuevo se establece como _this_ para la llamada de esa función.

A menos que la función retorne su propio objeto, la llamada retornará automáticamente el nuevo objeto creado.


    function foo(a) {
        this.a = a;
    }
    
    var bar = new foo(2);
    console.log(bar.a); // 2
        

###Bonus: Lexical this###

Este tipo de binding es posible a partir de **ES6** y se aplica al usar **arrow-functions.**

En este caso se usa el _lexical scope_ como _this_, es decir se tomara del contexto que encierre la llamada a la función.


    function foo() {
        return (a) => {
            console.log(this.a);
        };
    }
    
    var obj1 = {
        a: 2
    };
    
    var obj2 = {
        a: 3
    };
    
    var bar = foo.call(obj1);
    bar.call(obj2); // 2
        

Este tipo de binding ignora todas las reglas previamente mencionadas y no puede ser sobrescrito.


##Resumen##

Hemos visto que **_this_ es un binding** y que para determinarlo durante la ejecución de una función es necesario encontrar el call-site de esa función.


Una vez examinado, **4** reglas pueden aplicar en el siguiente orden:

1. ¿Se llamo usando **new**? Entonces se usa el objeto nuevo que se creo.

2. ¿Se llamo con **call o apply o bind**? Entonces se usa el objeto especificado.

3. ¿Se llamo bajo el contexto de un objeto? Entonces se usa ese objeto.

4. ¿Ninguna de las anteriores? **undefinded** en _strict mode_, sino el objeto global.

###Fuentes###

* [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
* [You Don't Know JS: this & Object Prototypes] (https://books.google.es/books?id=cE4BBAAAQBAJ&lpg=PP1&pg=PP1#v=onepage&q&f=false) _escrito por Kyle Simpson_