[Medium] 📄 this Binding
1. What is this in JavaScript?
¿Qué es
thisen JavaScript?
this es una palabra clave en JavaScript que apunta al objeto de contexto en el que se ejecuta una función. El valor de this depende de cómo se llama a la función, no de dónde se define.
Reglas de binding de this
Existen cuatro reglas para el binding de this en JavaScript (de mayor a menor prioridad):
- Binding new: Llamar a una función constructora con la palabra clave
new - Binding explícito: Especificar
thisexplícitamente usandocall,apply,bind - Binding implícito: Llamar como método de un objeto
- Binding por defecto: Comportamiento por defecto en otros casos
2. Please explain the difference of this in different contexts
Por favor, explique la diferencia de
thisen diferentes contextos
1. this en el entorno global
console.log(this); // Navegador: window, Node.js: global
function globalFunction() {
console.log(this); // Modo no estricto: window/global, modo estricto: undefined
}
globalFunction();
'use strict';
function strictFunction() {
console.log(this); // undefined
}
strictFunction();
2. this en funciones regulares (Function)
El this de una función regular depende de cómo se llama:
function regularFunction() {
console.log(this);
}
// Llamada directa: this apunta al objeto global (modo no estricto) o undefined (modo estricto)
regularFunction(); // window (modo no estricto) o undefined (modo estricto)
// Llamada como método de objeto: this apunta a ese objeto
const obj = {
method: regularFunction,
};
obj.method(); // obj
// Usando call/apply/bind: this apunta al objeto especificado
const customObj = { name: 'Custom' };
regularFunction.call(customObj); // customObj
3. this en Arrow Functions
Las Arrow Functions no tienen su propio this. Heredan el this del ámbito externo (ámbito léxico).
const obj = {
name: 'Object',
// Función regular
regularMethod: function () {
console.log('regularMethod this:', this); // obj
// Función regular interna: this cambia
function innerRegular() {
console.log('innerRegular this:', this); // window/undefined
}
innerRegular();
// Arrow Function interna: this se hereda del exterior
const innerArrow = () => {
console.log('innerArrow this:', this); // obj
};
innerArrow();
},
// Arrow Function
arrowMethod: () => {
console.log('arrowMethod this:', this); // window (hereda del global)
},
};
obj.regularMethod();
obj.arrowMethod();
4. this en métodos de objeto
const person = {
name: 'John',
age: 30,
// Función regular: this apunta a person
greet: function () {
console.log(`Hello, I'm ${this.name}`); // "Hello, I'm John"
},
// Método abreviado ES6: this apunta a person
introduce() {
console.log(`I'm ${this.name}, ${this.age} years old`);
},
// Arrow Function: this hereda del exterior (aquí es global)
arrowGreet: () => {
console.log(`Hello, I'm ${this.name}`); // "Hello, I'm undefined"
},
};
person.greet(); // "Hello, I'm John"
person.introduce(); // "I'm John, 30 years old"
person.arrowGreet(); // "Hello, I'm undefined"
5. this en funciones constructoras
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function () {
console.log(`Hello, I'm ${this.name}`);
};
}
const john = new Person('John', 30);
john.greet(); // "Hello, I'm John"
console.log(john.name); // "John"
6. this en clases
class Person {
constructor(name) {
this.name = name;
}
// Método regular: this apunta a la instancia
greet() {
console.log(`Hello, I'm ${this.name}`);
}
// Propiedad Arrow Function: this se vincula a la instancia
arrowGreet = () => {
console.log(`Hi, I'm ${this.name}`);
};
}
const john = new Person('John');
john.greet(); // "Hello, I'm John"
// Asignar el método a una variable pierde el binding de this
const greet = john.greet;
greet(); // Error: Cannot read property 'name' of undefined
// La propiedad Arrow Function no pierde el binding de this
const arrowGreet = john.arrowGreet;
arrowGreet(); // "Hi, I'm John"
3. Quiz: What will be printed?
Quiz: ¿Qué imprimirá el siguiente código?
Pregunta 1: Métodos de objeto y Arrow Functions
const obj = {
name: 'Object',
regularFunc: function () {
console.log('A:', this.name);
},
arrowFunc: () => {
console.log('B:', this.name);
},
};
obj.regularFunc();
obj.arrowFunc();
Haga clic para ver la respuesta
// A: Object
// B: undefined
Explicación:
regularFunces una función regular, llamada medianteobj.regularFunc(),thisapunta aobj, por lo que imprime"A: Object"arrowFunces una Arrow Function, no tiene su propiothis, hereda elthisdel exterior (global), no hay propiedadnameen el global, por lo que imprime"B: undefined"
Pregunta 2: Función pasada como argumento
const person = {
name: 'John',
greet: function () {
console.log(`Hello, ${this.name}`);
},
};
person.greet(); // 1
const greet = person.greet;
greet(); // 2
setTimeout(person.greet, 1000); // 3
Haga clic para ver la respuesta
// 1: "Hello, John"
// 2: "Hello, undefined" o error (modo estricto)
// 3: "Hello, undefined" o error (modo estricto)
Explicación:
person.greet()- Llamada a través del objeto,thisapunta apersongreet()- Al asignar el método a una variable y llamarlo directamente, se pierdethis, apunta a global oundefinedsetTimeout(person.greet, 1000)- El método se pasa como callback, se pierdethis
Pregunta 3: Funciones anidadas
const obj = {
name: 'Outer',
method: function () {
console.log('A:', this.name);
function inner() {
console.log('B:', this.name);
}
inner();
const arrow = () => {
console.log('C:', this.name);
};
arrow();
},
};
obj.method();
Haga clic para ver la respuesta
// A: Outer
// B: undefined
// C: Outer
Explicación:
A-methodse llama a través deobj,thisapunta aobjB-inneres una función regular, llamada directamente,thisapunta a global oundefinedC-arrowes una Arrow Function, hereda elthisdelmethodexterno, apunta aobj
Pregunta 4: setTimeout y Arrow Functions
const obj = {
name: 'Object',
method1: function () {
setTimeout(function () {
console.log('A:', this.name);
}, 100);
},
method2: function () {
setTimeout(() => {
console.log('B:', this.name);
}, 100);
},
};
obj.method1();
obj.method2();
Haga clic para ver la respuesta
// A: undefined
// B: Object
Explicación:
A- El callback desetTimeoutes una función regular, al ejecutarsethisapunta al globalB- El callback desetTimeoutes una Arrow Function, hereda elthisdelmethod2externo, apunta aobj
Pregunta 5: Binding complejo de this
const obj1 = {
name: 'obj1',
getThis: function () {
return this;
},
};
const obj2 = {
name: 'obj2',
};
console.log('A:', obj1.getThis().name);
const getThis = obj1.getThis;
console.log('B:', getThis() === window); // Asumiendo entorno de navegador
obj2.getThis = obj1.getThis;
console.log('C:', obj2.getThis().name);
const boundGetThis = obj1.getThis.bind(obj2);
console.log('D:', boundGetThis().name);
Haga clic para ver la respuesta
// A: obj1
// B: true
// C: obj2
// D: obj2
Explicación:
A- Llamada a través deobj1,thisapunta aobj1B- Llamada directa,thisapunta a global (window)C- Llamada a través deobj2,thisapunta aobj2D- Usandobindpara vincularthisaobj2
Pregunta 6: Función constructora y prototipo
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
console.log(`Hello, I'm ${this.name}`);
};
Person.prototype.delayedGreet = function () {
setTimeout(function () {
console.log('A:', this.name);
}, 100);
};
Person.prototype.arrowDelayedGreet = function () {
setTimeout(() => {
console.log('B:', this.name);
}, 100);
};
const john = new Person('John');
john.delayedGreet();
john.arrowDelayedGreet();
Haga clic para ver la respuesta
// A: undefined
// B: John
Explicación:
A- Callback de función regular desetTimeout,thisapunta al globalB- Callback de Arrow Function desetTimeout, hereda elthisdelarrowDelayedGreetexterno, apunta ajohn
Pregunta 7: Variable global vs propiedad de objeto
var name = 'jjjj';
var obj = {
a: function () {
name = 'john';
console.log(this.name);
},
};
obj.a();
Haga clic para ver la respuesta
// undefined
Explicación:
La clave de esta pregunta está en entender la diferencia entre variables globales y propiedades de objeto:
-
A qué apunta
thisenobj.a():- Llamada mediante
obj.a(),thisapunta aobj
- Llamada mediante
-
name = 'john'modifica la variable global:name = 'john'; // Sin var/let/const, modifica el name global
// Equivale a
window.name = 'john'; // Entorno de navegador -
this.nameaccede a la propiedad del objeto:console.log(this.name); // Equivale a console.log(obj.name) -
El objeto
objno tiene propiedadname:obj.name; // undefined (name no está definido dentro del objeto obj)
Proceso de ejecución completo:
// Estado inicial
window.name = 'jjjj'; // Variable global
obj = {
a: function () { /* ... */ },
// Nota: ¡obj no tiene propiedad name!
};
// Ejecución de obj.a()
obj.a();
↓
// 1. name = 'john' → Modifica el window.name global
window.name = 'john'; // ✅ Variable global modificada
// 2. this.name → Accede a obj.name
this.name; // Equivale a obj.name
obj.name; // undefined (obj no tiene propiedad name)
Malentendidos comunes:
Muchos piensan que se imprimirá 'john', porque:
- ❌ Creen erróneamente que
name = 'john'añadirá una propiedad aobj - ❌ Creen erróneamente que
this.nameaccederá a la variable global
Entendimiento correcto:
- ✅
name = 'john'solo modifica la variable global, no afecta aobj - ✅
this.nameaccede aobj.name, no alnameglobal
Para imprimir 'john', se debería escribir así:
var obj = {
a: function () {
this.name = 'john'; // ✅ Añade propiedad name a obj
console.log(this.name); // 'john'
},
};
obj.a(); // Imprime 'john'
console.log(obj.name); // 'john'
Pregunta 8: Trampa de variables globales (extensión)
var name = 'global';
var obj = {
name: 'object',
a: function () {
name = 'modified'; // Nota: sin var/let/const
console.log('1:', name); // Accede a variable global
console.log('2:', this.name); // Accede a propiedad del objeto
},
};
obj.a();
console.log('3:', name); // Variable global
console.log('4:', obj.name); // Propiedad del objeto
Haga clic para ver la respuesta
// 1: modified
// 2: object
// 3: modified
// 4: object
Explicación:
// Estado inicial
window.name = 'global'; // Variable global
obj.name = 'object'; // Propiedad del objeto
// Ejecución de obj.a()
name = 'modified'; // Modifica el window.name global
console.log('1:', name); // Acceso global: 'modified'
console.log('2:', this.name); // Acceso a obj.name: 'object'
// Después de la ejecución
console.log('3:', name); // Global: 'modified'
console.log('4:', obj.name); // Objeto: 'object' (no modificado)
Conceptos clave:
name(sinthis.) → Accede a la variable globalthis.name(conthis.) → Accede a la propiedad del objeto- ¡Son variables completamente diferentes!
4. How to preserve this in callbacks?
¿Cómo preservar
thisen funciones callback?
Método 1: Usar Arrow Functions
const obj = {
name: 'Object',
method: function () {
// ✅ La Arrow Function hereda this del exterior
setTimeout(() => {
console.log(this.name); // "Object"
}, 1000);
},
};
obj.method();
Método 2: Usar bind()
const obj = {
name: 'Object',
method: function () {
// ✅ bind vincula this
setTimeout(
function () {
console.log(this.name); // "Object"
}.bind(this),
1000
);
},
};
obj.method();
Método 3: Guardar this en una variable (método antiguo)
const obj = {
name: 'Object',
method: function () {
// ✅ Guardar this en una variable
const self = this;
setTimeout(function () {
console.log(self.name); // "Object"
}, 1000);
},
};
obj.method();
Método 4: Usar call() o apply()
function greet() {
console.log(`Hello, I'm ${this.name}`);
}
const person1 = { name: 'John' };
const person2 = { name: 'Jane' };
greet.call(person1); // "Hello, I'm John"
greet.apply(person2); // "Hello, I'm Jane"
5. Common this pitfalls
Trampas comunes de
this
Trampa 1: Asignar método de objeto a una variable
const obj = {
name: 'Object',
greet: function () {
console.log(this.name);
},
};
obj.greet(); // ✅ "Object"
const greet = obj.greet;
greet(); // ❌ undefined (this se pierde)
// Solución: usar bind
const boundGreet = obj.greet.bind(obj);
boundGreet(); // ✅ "Object"
Trampa 2: this en event listeners
const button = document.querySelector('button');
const obj = {
name: 'Object',
// ❌ Arrow Function: this no apunta a button
handleClick1: () => {
console.log(this); // window
},
// ✅ Función regular: this apunta al elemento que disparó el evento
handleClick2: function () {
console.log(this); // elemento button
},
// ✅ Si necesita acceder al this del objeto, envolver con Arrow Function
handleClick3: function () {
button.addEventListener('click', () => {
console.log(this.name); // "Object"
});
},
};
Trampa 3: Callbacks en métodos de arrays
const obj = {
name: 'Object',
items: [1, 2, 3],
// ❌ El callback de función regular pierde this
processItems1: function () {
this.items.forEach(function (item) {
console.log(this.name, item); // undefined 1, undefined 2, undefined 3
});
},
// ✅ El callback de Arrow Function preserva this
processItems2: function () {
this.items.forEach((item) => {
console.log(this.name, item); // "Object" 1, "Object" 2, "Object" 3
});
},
// ✅ Usar el parámetro thisArg de forEach
processItems3: function () {
this.items.forEach(function (item) {
console.log(this.name, item); // "Object" 1, "Object" 2, "Object" 3
}, this); // El segundo parámetro especifica this
},
};
6. this binding rules summary
Resumen de las reglas de binding de
this
Prioridad (de mayor a menor)
// 1. Binding new (mayor prioridad)
function Person(name) {
this.name = name;
}
const john = new Person('John');
console.log(john.name); // "John"
// 2. Binding explícito (call/apply/bind)
function greet() {
console.log(this.name);
}
const obj = { name: 'Object' };
greet.call(obj); // "Object"
// 3. Binding implícito (método de objeto)
const obj2 = {
name: 'Object2',
greet: greet,
};
obj2.greet(); // "Object2"
// 4. Binding por defecto (menor prioridad)
greet(); // undefined (modo estricto) o window.name
Tabla comparativa: Function vs Arrow Function
| Característica | Function | Arrow Function |
|---|---|---|
Tiene su propio this | ✅ Sí | ❌ No |
this depende de | Forma de llamada | Lugar de definición (ámbito léxico) |
Se puede cambiar this con call/apply/bind | ✅ Sí | ❌ No |
| Se puede usar como constructor | ✅ Sí | ❌ No |
Tiene objeto arguments | ✅ Sí | ❌ No |
| Adecuada para | Métodos de objeto, constructores | Callbacks, cuando se necesita heredar this externo |
Regla mnemotécnica
"Arrow hereda, función llama"
- Arrow Function:
thishereda del ámbito externo, se determina en la definición- Función regular:
thisdepende de cómo se llama, se determina en la ejecución
7. Best practices
Mejores prácticas
✅ Prácticas recomendadas
// 1. Usar funciones regulares o métodos abreviados ES6 para métodos de objeto
const obj = {
name: 'Object',
// ✅ Bien: Función regular
greet: function () {
console.log(this.name);
},
// ✅ Bien: Abreviatura ES6
introduce() {
console.log(this.name);
},
};
// 2. Usar Arrow Functions para callbacks
class Component {
constructor() {
this.name = 'Component';
}
mount() {
// ✅ Bien: Arrow Function preserva this
setTimeout(() => {
console.log(this.name);
}, 1000);
}
}
// 3. Usar funciones regulares cuando se necesita this dinámico
const button = {
label: 'Click me',
// ✅ Bien: Necesita acceder al this del elemento DOM
handleClick: function () {
console.log(this); // elemento DOM button
},
};
❌ Prácticas no recomendadas
// 1. No usar Arrow Functions para métodos de objeto
const obj = {
name: 'Object',
// ❌ Mal: this no apunta a obj
greet: () => {
console.log(this.name); // undefined
},
};
// 2. No usar Arrow Functions como constructores
// ❌ Mal: Arrow Functions no pueden ser constructores
const Person = (name) => {
this.name = name; // ¡Error!
};
// 3. No usar Arrow Functions cuando se necesita acceder a arguments
// ❌ Mal: Arrow Functions no tienen arguments
const sum = () => {
console.log(arguments); // ReferenceError
};