La palabra clave this en JavaScript a menudo ha sido una fuente de mucha confusión para los principiantes en el lenguaje. Parte de esta confusión proviene del hecho de que esto en JavaScript se trata de manera diferente en comparación con otros lenguajes como Java o Python.
Comprender esto es absolutamente imperativo para comprender conceptos más avanzados en JavaScript o para leer y escribir código JavaScript, por lo que dedicaremos este artículo a tratar de aclarar qué significa realmente esto en JavaScript.
Pasaremos gran parte del artículo aprendiendo sobre esto con referencia a las funciones en JavaScript, por lo que primero veremos un hecho sobre las funciones de JavaScript que nos ayudará a hacerlo mejor.
esto y funciones
Las funciones, en JavaScript, son esencialmente objetos. Al igual que los objetos, pueden asignarse a variables, pasarse a otras funciones y devolverse desde funciones. Y al igual que los objetos, tienen sus propias propiedades. Una de estas propiedades es esta .
El valor que almacena es el contexto de ejecución actual del programa JavaScript. Por lo tanto, cuando se usa dentro de una función , el valor de este cambiará dependiendo de cómo se defina esa función, cómo se invoque y el contexto de ejecución predeterminado.
Nota: esto siempre contiene la referencia a un solo objeto, que define el contexto de ejecución de la línea actual de código.
Antes de profundizar en cómo se comporta esto en las funciones, veamos cómo se comporta fuera de ellas:
Contexto global:
se dice que una línea de código escrita fuera de una función pertenece al contexto global y el valor de esto en este contexto global es igual que el objeto global.
Por ejemplo, si abriera la consola de su navegador y escribiera las siguientes líneas en él, y presionara regresar/ingresar:
console.log(this) Vería
que el objeto Ventana se registra en la consola. Esto se debe a que el objeto global, en un tiempo de ejecución del navegador, como el tiempo de ejecución de Chrome, es el objeto Ventana.
Sin embargo, dentro de una función, es posible que el contexto global ya no esté presente y que la función tenga su propio contexto definido y, por lo tanto, un valor diferente de este. Para entender eso, volvamos nuestra atención a las funciones: Las
funciones, en JavaScript, se pueden invocar de varias maneras: 1.
Invocación de funciones
2. Invocación de métodos 3. Invocación de
constructores
esto con invocación de función:
La invocación de función se refiere al proceso de invocación de una función usando su nombre o una expresión que se evalúa como el objeto de la función seguido de un conjunto de primeros corchetes de apertura y cierre (la inclusión de los corchetes indica que le estamos pidiendo al motor de JavaScript que ejecute la función de inmediato ).
Por ejemplo:
JavaScript
<!DOCTYPE html> <html> <body> <script> function doSomething() { // do something here } // function invocation doSomething(); </script> </body> </html>
esto dentro de la función doSomething, si se invoca a través de la invocación de la función como se indicó anteriormente, tiene el valor del objeto global, que es el objeto de la ventana en el entorno del navegador:
JavaScript
<!DOCTYPE html> <html> <body> <script> function doSomething(a, b) { // adds a propone property to the Window object this.propone = "test value"; } // function invocation doSomething(); document.write(window.propone); </script> </body> </html>
Producción:
test value
Sin embargo, este no es siempre el caso. Si la función doSomething() se ejecutara en modo estricto , registraría undefined en lugar del objeto de ventana global. Esto se debe a que, en modo estricto (indicado por la línea: ‘use strict’; ), el valor predeterminado de this, cualquier objeto de función se establece en indefinido en lugar del objeto global.
Por ejemplo :
JavaScript
<!DOCTYPE html> <html> <body> <script> function doSomething() { // enable the strict mode 'use strict'; // logs undefined document.write(this + '<br>') function innerFunction() { // Also logs undefined, indicating that // strict mode permeates to inner function scopes document.write(this) } innerFunction(); } // function invocation doSomething(); </script> </body> </html>
Producción:
undefined undefined
esto con la invocación del método:
Las funciones, cuando se definen como campos o propiedades de objetos, se denominan métodos.
JavaScript
<!DOCTYPE html> <html> <body> <script> let person = { name : "John", age : 31, logInfo : function() { document.write(this.name + " is " + this.age + " years old "); } } // logs John is 31 years old person.logInfo() </script> </body> </html>
Producción:
John is 31 years old
En el ejemplo de código anterior, logInfo() es un método del objeto persona y lo invocamos usando el patrón de invocación del objeto.
Es decir, usamos los descriptores de acceso a la propiedad para acceder al método que formaba parte del objeto.
Tal invocación requiere el uso de una expresión que evalúe el objeto del cual nuestro método es parte, y un descriptor de acceso a la propiedad (Ej.: person.logInfo() ) seguido de un conjunto de paréntesis de apertura y cierre.
Es esencial comprender cómo difieren las invocaciones de funciones y las invocaciones de métodos.
Esto, a su vez, nos ayudará a comprender cuál podría ser este contexto en cualquier función dada, porque en cada una de estas invocaciones, el valor deesto es diferente
Dentro de dicho método, que ha sido invocado usando la propiedad de acceso, tendrá el valor del objeto invocador, es decir, apuntará al objeto que se usó junto con la propiedad de acceso para realizar la llamada .
Por ejemplo :
JavaScript
<!DOCTYPE html> <html> <body> <script> let add = { num : 0, calc : function() { // logs the add object document.write(this + ' ') this.num += 1; return this.num; } }; // logs 1 document.write(add.calc() + '<br>'); // logs 2 document.write(add.calc()); </script> </body> </html>
Producción:
[object Object] 1 [object Object] 2
En el ejemplo anterior, calc() es un método del objeto add y, por lo tanto, se llama usando las reglas de invocación de métodos en las líneas 9 y 10.
Y sabemos que, cuando se usan patrones de invocación de métodos, el valor de this se establece en la llamada objeto.
Dentro de este método calc(), el valor de this se establece en el objeto que llama, que en nuestro caso es add. y así podemos acceder con éxito a la propiedad num de add.
Sin embargo, veamos un punto de confusión importante:
¿Qué sucede con esto en una función anidada dentro de un método de un objeto?
JavaScript
<!DOCTYPE html> <html> <body> <script> let add = { num : 0, calc : function() { // logs the add object document.write(this + ' ') function innerfunc() { this.num += 1; // logs the window object document.write(this + ' '); return this.num } return innerfunc(); } }; // logs NaN document.write(add.calc() + '<br>'); // logs NaN document.write(add.calc()); </script> </body> </html>
Producción:
[object Object] [object Window] NaN [object Object] [object Window] NaN
Tratemos de entender lo que acaba de suceder.
Cuando llamamos a calc() en las líneas 14 y 15, estamos usando la invocación del método que configura esto para agregar en calc(). Esto se puede verificar usando la declaración de registro en la línea 4.
Sin embargo, se llama a innerfunc() desde dentro del método calc() usando una invocación de función simple (línea 11). Esto significa que, dentro de innerfunc(), se establece en el objeto global, que no tiene una propiedad num, y por lo tanto se obtienen los resultados de NaN.
¿Cómo resolvemos este problema? ¿Cómo podemos retener el valor de this desde el método externo dentro de la función anidada?
Una solución es asignar este valor de la función externa a una variable que se usará en la función anidada de la siguiente manera:
JavaScript
<!DOCTYPE html> <html> <body> <script> let add = { num : 0, calc : function() { // logs the add object document.write(this + ' ') // using thisreference variable to // store the value of this thisreference = this; function innerfunc() { // using the variable to access the // context of the outer function thisreference.num += 1; // logs the add object document.write(thisreference + ' '); return thisreference.num; } return innerfunc(); } }; // logs 1 document.write(add.calc() + '<br>'); // logs 2 document.write(add.calc()); </script> </body> </html>
Producción:
[object Object] [object Object] 1 [object Object] [object Object] 2
Otras soluciones a este problema incluyen el uso de bind(), call() o apply(), que veremos pronto.
esto con la invocación del constructor:
La invocación del constructor se realiza cuando la nueva palabra clave va seguida de un nombre de función y un conjunto de paréntesis de apertura y cierre (con o sin argumentos).
Por ejemplo: let person1= new People(‘Juan’, 21);
Aquí, person1 es el objeto recién creado y People es la función constructora utilizada para crear este objeto.
La invocación del constructor es una de las varias formas de crear objetos en JavaScript.
¿Qué sucede exactamente cuando usamos la nueva palabra clave junto con el nombre de una función?
Hay esencialmente cinco pasos involucrados en la creación de un objeto a través de este método. Vamos a estudiarlos con el siguiente ejemplo:
JavaScript
<!DOCTYPE html> <html> <body> <script> let people = function(name, age) { this.name = name; this.age = age; this.displayInfo = function() { document.write(this.name + " is " + this.age + " years old"); } } let person1 = new people('John', 21); // logs John is 21 years old person1.displayInfo(); </script> </body> </html>
Producción:
John is 21 years old
- En primer lugar, se crea un objeto vacío que es una instancia del nombre de función utilizado con nuevo (es decir, personas (nombre, edad)). En otras palabras, establece la propiedad constructora del objeto en la función utilizada en la invocación (personas (nombre, edad)).
- Luego, vincula el prototipo de la función constructora (personas) al objeto recién creado, asegurando así que este objeto pueda heredar todas las propiedades y métodos de la función constructora.
- Luego, se llama a la función constructora en este objeto recién creado. Si recordamos la invocación del método, veremos que es similar. Por lo tanto, dentro de la función constructora, esta obtiene el valor del objeto recién creado utilizado en la llamada.
- Finalmente, el objeto creado, con todas sus propiedades y métodos establecidos, se devuelve a person1
Navegador compatible:
- Google Chrome
- Borde de Microsoft
- Firefox
- Ópera
- Safari