Cuando invocamos una función en Javascript, junto a los parámetros que decidamos pasarle, se pasa un parámetro “escondido” llamado this, y del que disponemos dentro de la función (y que veréis que se bastante). Siguiendo la clasificación que el autor del libro “Javascript: the good parts” ofrece, hay 4 patrones de invocación que podemos usar a la hora de llamar a una función, y el valor de “this” depende de qué patrón usemos.

1. Invocación

Cuando usamos este patrón, this SIEMPRE es una referencia al objeto global

// Definimos una variable global
var global = "Hola mundo";

function test() {
    return this.global;
}

// Usemos el patrón "invocación"
var test_var = test();
console.log(test_var); // "Hola mundo";

// Otro ejemplo más común de este patrón (aunque no utiliza el parámetro this)
function hello(name) {
    return "Hello " + name + "!";
}
console.log(hello("Carlos")); // Hello Carlos!

El problema es que mucha gente considera que este comportamiento es un gran problema en Javascript. Defienden que “this” debería ser una referencia a la propiedad “this” de la función donde se definió esa función (menudo travalenguas), y no siempre al objeto global.

// Definimos una variable global (en el objeto global)
var name = "Carlos";

// Definimos un objeto con una función y una propiedad
var obj = {
    name: "Manolo",
    test: function () {
        // Definimos una función auxiliar aquí
        function hello() {
            console.log(this.name);
        }
        // Al llamarla, ¿Debería mostrarse "Manolo" o "Carlos"?
        // => this referencia siempre al objeto global
        hello();
    }
}

// Llamamos a la función con el patrón invocacion de método
// (Es el siguiente patrón)
obj.test(); // "Carlos"

Una forma de solucionar este problema es ignorar la variable this, y hacer la nuestra propia, ya que la función tiene acceso a todas las variables definidas en el scope donde se definió la función.
Es una solución muy útil para cuando “this” no tiene el valor que necesitas pero estás forzado a usar ese patrón de invocación.

// Definimos una variable global (en el objeto global)
var name = "Carlos";

// Definimos un objeto con una función y una propiedad
var obj = {
    name: "Manolo",
    test: function () {
        // Creemos nuestra propia version de this
        var that = this;

        // Definimos una función
        function hello() {
            // Ignoremos this ya que no nos interesa
            // Aqui podemos acceder a that
            console.log(that.name);
        }
        // Al llamarla, ¿Debería mostrarse "Manolo" o "Carlos"?
        // Al usar that, accedemos al objeto obj.
        hello();
    }
}

// Llamamos a la función con el patrón invocacion de método
// (Es el siguiente patrón)
obj.test(); // "Manolo"

2. Invocación de método

Cuando lo que invocamos es un método (una función que es miembro de un objeto), la variable this hace referencia al objeto. Veamos el mismo ejemplo de antes pero con esté patrón.

// Definimos un objeto con una función y una propiedad
var obj = {
    name: "Manolo",
    test: function () {
        console.log(this.name);
    }
}

// Llamamos a la función con el patrón invocacion de método
// Por lo que this, dentro de la función, será una referencia a "obj".
obj.test(); // "Manolo"

Fíjate bien en la diferencia, porque puede no verse a simple vista. En este caso la función que ejecuta “console.log” es una propiedad del objeto obj (un método). En el caso anterior, sin embargo, la función que ejecutaba “console.log” era una función definida dentro de un método (y no un método en sí misma).

3. Constructor

Para utilizar un constructor usamos la palabra clave new (parecido a otros lenguajes de programación). Lo que ocurre internamente cuando usamos este patrón es lo siguiente:

  • Se crea un nuevo objeto
  • Establece el “prototype” de ese objeto al “prototype” del constructor
  • Ejecuta el constructor, donde “this” es una referencia al objeto recien creado
  • Devuelve “this” (el objeto creado)

Si no sabes qué es “prototype”, lo intentaré abordar en la cuarta parte.

Las funciones constructor están hechas para que sean llamadas exclusivamente con la palabra clave “new”. Para evitar que alguien las llame con otro patrón se suele indicar que una función es un constructor colocando su primera letra en mayúscula.

// Definimos un constructor
var Constructor = function () {
    this.name = "Carlos";
    this.hello = function () {
        console.log("Hello " + this.name);
    }
    // "return this;" queda implícito cuando
    // se trata de un constructor
}

// Lo llamamos con el patrón constructor
var MiObj = new Constructor();
console.log(MiObj.name); // "Carlos"
MiObj.hello(); // "Hello Carlos"

4. La función apply

El último patrón es una función de Javascript que nos permite especificar exactamente a qué queremos enlazar this, y qué parametros queremos pasarle a la función.

// Definimos un objeto que usaremos como this, con una propiedad
var obj = {
    name: "Carlos"
}

// Definimos una funcion que usa parametros propios y this
function hello(msg) {
    console.log("Ey " + this.name + msg);
}

// La llamamos con apply, donde especificamos el valor de this
hello.apply(this, [", what are you doing?"]);

Eso es todo, en la tercera parte intentaré explicar un poco formas y patrones para organizar el código en librerias javascript.

La referencia más clara para escribir este artículo ha sido Javascript, the good parts, libro que en su día me recomendaron y que yo ahora recomiendo a todos los que os interese Javascript. Creo que no tiene traducción al español, pero con un poco de inglés técnico creo que es suficiente.

Si se te pasó la primera parte, puedes rescatarla en el siguiente enlace: Javascript 1: Características generales