[Medium] 📄 this Binding
1. What is this in JavaScript?
O que é
thisem JavaScript?
this é uma palavra-chave em JavaScript que aponta para o objeto de contexto no qual uma função é executada. O valor de this depende de como a função é chamada, e não de onde ela é definida.
Regras de binding do this
Existem quatro regras de binding para this em JavaScript (da maior para a menor prioridade):
- Binding new: Chamar uma função construtora com a palavra-chave
new - Binding explícito: Especificar
thisexplicitamente usandocall,apply,bind - Binding implícito: Chamar como método de um objeto
- Binding padrão: Comportamento padrão em outros casos
2. Please explain the difference of this in different contexts
Por favor, explique a diferença do
thisem diferentes contextos
1. this no ambiente global
console.log(this); // Navegador: window, Node.js: global
function globalFunction() {
console.log(this); // Modo não estrito: window/global, modo estrito: undefined
}
globalFunction();
'use strict';
function strictFunction() {
console.log(this); // undefined
}
strictFunction();
2. this em funções regulares (Function)
O this de uma função regular depende de como ela é chamada:
function regularFunction() {
console.log(this);
}
// Chamada direta: this aponta para o objeto global (modo não estrito) ou undefined (modo estrito)
regularFunction(); // window (modo não estrito) ou undefined (modo estrito)
// Chamada como método de objeto: this aponta para esse objeto
const obj = {
method: regularFunction,
};
obj.method(); // obj
// Usando call/apply/bind: this aponta para o objeto especificado
const customObj = { name: 'Custom' };
regularFunction.call(customObj); // customObj
3. this em Arrow Functions
Arrow Functions não possuem seu próprio this. Elas herdam o this do escopo externo (escopo léxico).
const obj = {
name: 'Object',
// Função regular
regularMethod: function () {
console.log('regularMethod this:', this); // obj
// Função regular interna: this muda
function innerRegular() {
console.log('innerRegular this:', this); // window/undefined
}
innerRegular();
// Arrow Function interna: this é herdado do exterior
const innerArrow = () => {
console.log('innerArrow this:', this); // obj
};
innerArrow();
},
// Arrow Function
arrowMethod: () => {
console.log('arrowMethod this:', this); // window (herda do global)
},
};
obj.regularMethod();
obj.arrowMethod();
4. this em métodos de objeto
const person = {
name: 'John',
age: 30,
// Função regular: this aponta para person
greet: function () {
console.log(`Hello, I'm ${this.name}`); // "Hello, I'm John"
},
// Método abreviado ES6: this aponta para person
introduce() {
console.log(`I'm ${this.name}, ${this.age} years old`);
},
// Arrow Function: this herda do exterior (aqui é o 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 em funções construtoras
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 em classes
class Person {
constructor(name) {
this.name = name;
}
// Método regular: this aponta para a instância
greet() {
console.log(`Hello, I'm ${this.name}`);
}
// Propriedade Arrow Function: this é vinculado à instância
arrowGreet = () => {
console.log(`Hi, I'm ${this.name}`);
};
}
const john = new Person('John');
john.greet(); // "Hello, I'm John"
// Atribuir o método a uma variável perde o binding do this
const greet = john.greet;
greet(); // Erro: Cannot read property 'name' of undefined
// A propriedade Arrow Function não perde o binding do this
const arrowGreet = john.arrowGreet;
arrowGreet(); // "Hi, I'm John"
3. Quiz: What will be printed?
Quiz: O que o código a seguir imprimirá?
Questão 1: Métodos de objeto e Arrow Functions
const obj = {
name: 'Object',
regularFunc: function () {
console.log('A:', this.name);
},
arrowFunc: () => {
console.log('B:', this.name);
},
};
obj.regularFunc();
obj.arrowFunc();
Clique para ver a resposta
// A: Object
// B: undefined
Explicação:
regularFuncé uma função regular, chamada viaobj.regularFunc(),thisaponta paraobj, então imprime"A: Object"arrowFuncé uma Arrow Function, não tem seu própriothis, herda othisdo exterior (global). Não existe propriedadenameno global, então imprime"B: undefined"
Questão 2: Função passada 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
Clique para ver a resposta
// 1: "Hello, John"
// 2: "Hello, undefined" ou erro (modo estrito)
// 3: "Hello, undefined" ou erro (modo estrito)
Explicação:
person.greet()- Chamada através do objeto,thisaponta parapersongreet()- Após atribuir o método a uma variável e chamá-lo diretamente,thisé perdido, aponta para global ouundefinedsetTimeout(person.greet, 1000)- O método é passado como callback,thisé perdido
Questão 3: Funções aninhadas
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();
Clique para ver a resposta
// A: Outer
// B: undefined
// C: Outer
Explicação:
A-methodé chamada viaobj,thisaponta paraobjB-inneré uma função regular, chamada diretamente,thisaponta para global ouundefinedC-arrowé uma Arrow Function, herda othisdomethodexterno, aponta paraobj
Questão 4: setTimeout e 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();
Clique para ver a resposta
// A: undefined
// B: Object
Explicação:
A- O callback dosetTimeouté uma função regular, na execuçãothisaponta para o globalB- O callback dosetTimeouté uma Arrow Function, herda othisdomethod2externo, aponta paraobj
Questão 5: Binding complexo do 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); // Assumindo ambiente de navegador
obj2.getThis = obj1.getThis;
console.log('C:', obj2.getThis().name);
const boundGetThis = obj1.getThis.bind(obj2);
console.log('D:', boundGetThis().name);
Clique para ver a resposta
// A: obj1
// B: true
// C: obj2
// D: obj2
Explicação:
A- Chamada viaobj1,thisaponta paraobj1B- Chamada direta,thisaponta para global (window)C- Chamada viaobj2,thisaponta paraobj2D- Usandobindpara vincularthisaobj2
Questão 6: Função construtora e protótipo
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();
Clique para ver a resposta
// A: undefined
// B: John
Explicação:
A- Callback de função regular dosetTimeout,thisaponta para o globalB- Callback de Arrow Function dosetTimeout, herda othisdoarrowDelayedGreetexterno, aponta parajohn
Questão 7: Variável global vs propriedade de objeto
var name = 'jjjj';
var obj = {
a: function () {
name = 'john';
console.log(this.name);
},
};
obj.a();
Clique para ver a resposta
// undefined
Explicação:
A chave desta questão está em entender a diferença entre variáveis globais e propriedades de objeto:
-
Para onde
thisaponta emobj.a():- Chamada via
obj.a(),thisaponta paraobj
- Chamada via
-
name = 'john'modifica a variável global:name = 'john'; // Sem var/let/const, modifica o name global
// Equivale a
window.name = 'john'; // Ambiente de navegador -
this.nameacessa a propriedade do objeto:console.log(this.name); // Equivale a console.log(obj.name) -
O objeto
objnão tem propriedadename:obj.name; // undefined (name não está definido dentro do objeto obj)
Processo de execução completo:
// Estado inicial
window.name = 'jjjj'; // Variável global
obj = {
a: function () { /* ... */ },
// Nota: obj não tem propriedade name!
};
// Execução de obj.a()
obj.a();
↓
// 1. name = 'john' → Modifica o window.name global
window.name = 'john'; // ✅ Variável global modificada
// 2. this.name → Acessa obj.name
this.name; // Equivale a obj.name
obj.name; // undefined (obj não tem propriedade name)
Mal-entendidos comuns:
Muitos pensam que será impresso 'john', porque:
- ❌ Acreditam erroneamente que
name = 'john'adicionará uma propriedade aobj - ❌ Acreditam erroneamente que
this.nameacessará a variável global
Entendimento correto:
- ✅
name = 'john'apenas modifica a variável global, não afetaobj - ✅
this.nameacessaobj.name, não onameglobal
Para imprimir 'john', deveria ser escrito assim:
var obj = {
a: function () {
this.name = 'john'; // ✅ Adiciona propriedade name a obj
console.log(this.name); // 'john'
},
};
obj.a(); // Imprime 'john'
console.log(obj.name); // 'john'
Questão 8: Armadilha de variáveis globais (extensão)
var name = 'global';
var obj = {
name: 'object',
a: function () {
name = 'modified'; // Nota: sem var/let/const
console.log('1:', name); // Acessa variável global
console.log('2:', this.name); // Acessa propriedade do objeto
},
};
obj.a();
console.log('3:', name); // Variável global
console.log('4:', obj.name); // Propriedade do objeto
Clique para ver a resposta
// 1: modified
// 2: object
// 3: modified
// 4: object
Explicação:
// Estado inicial
window.name = 'global'; // Variável global
obj.name = 'object'; // Propriedade do objeto
// Execução de obj.a()
name = 'modified'; // Modifica o window.name global
console.log('1:', name); // Acesso global: 'modified'
console.log('2:', this.name); // Acesso a obj.name: 'object'
// Após a execução
console.log('3:', name); // Global: 'modified'
console.log('4:', obj.name); // Objeto: 'object' (não modificado)
Conceitos-chave:
name(semthis.) → Acessa a variável globalthis.name(comthis.) → Acessa a propriedade do objeto- Os dois são variáveis completamente diferentes!
4. How to preserve this in callbacks?
Como preservar
thisem funções callback?
Método 1: Usar Arrow Functions
const obj = {
name: 'Object',
method: function () {
// ✅ Arrow Function herda this do 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: Salvar this em uma variável (método antigo)
const obj = {
name: 'Object',
method: function () {
// ✅ Salvar this em uma variável
const self = this;
setTimeout(function () {
console.log(self.name); // "Object"
}, 1000);
},
};
obj.method();
Método 4: Usar call() ou 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
Armadilhas comuns do
this
Armadilha 1: Atribuir método de objeto a uma variável
const obj = {
name: 'Object',
greet: function () {
console.log(this.name);
},
};
obj.greet(); // ✅ "Object"
const greet = obj.greet;
greet(); // ❌ undefined (this é perdido)
// Solução: usar bind
const boundGreet = obj.greet.bind(obj);
boundGreet(); // ✅ "Object"
Armadilha 2: this em event listeners
const button = document.querySelector('button');
const obj = {
name: 'Object',
// ❌ Arrow Function: this não aponta para button
handleClick1: () => {
console.log(this); // window
},
// ✅ Função regular: this aponta para o elemento que disparou o evento
handleClick2: function () {
console.log(this); // elemento button
},
// ✅ Se precisar acessar o this do objeto, envolva com Arrow Function
handleClick3: function () {
button.addEventListener('click', () => {
console.log(this.name); // "Object"
});
},
};
Armadilha 3: Callbacks em métodos de arrays
const obj = {
name: 'Object',
items: [1, 2, 3],
// ❌ Callback de função regular perde this
processItems1: function () {
this.items.forEach(function (item) {
console.log(this.name, item); // undefined 1, undefined 2, undefined 3
});
},
// ✅ Callback de Arrow Function preserva this
processItems2: function () {
this.items.forEach((item) => {
console.log(this.name, item); // "Object" 1, "Object" 2, "Object" 3
});
},
// ✅ Usar o parâmetro thisArg do forEach
processItems3: function () {
this.items.forEach(function (item) {
console.log(this.name, item); // "Object" 1, "Object" 2, "Object" 3
}, this); // O segundo parâmetro especifica this
},
};
6. this binding rules summary
Resumo das regras de binding do
this
Prioridade (da mais alta para a mais baixa)
// 1. Binding new (maior prioridade)
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 padrão (menor prioridade)
greet(); // undefined (modo estrito) ou window.name
Tabela comparativa: Function vs Arrow Function
| Característica | Function | Arrow Function |
|---|---|---|
Tem seu próprio this | ✅ Sim | ❌ Não |
this depende de | Forma de chamada | Local de definição (escopo léxico) |
Pode mudar this com call/apply/bind | ✅ Sim | ❌ Não |
| Pode ser usada como construtora | ✅ Sim | ❌ Não |
Tem objeto arguments | ✅ Sim | ❌ Não |
| Adequada para | Métodos de objeto, construtoras | Callbacks, quando precisa herdar this externo |
Dica de memorização
"Arrow herda, função chama"
- Arrow Function:
thisherda do escopo externo, determinado na definição- Função regular:
thisdepende de como é chamada, determinado na execução
7. Best practices
Boas práticas
✅ Práticas recomendadas
// 1. Usar funções regulares ou sintaxe abreviada ES6 para métodos de objeto
const obj = {
name: 'Object',
// ✅ Bom: Função regular
greet: function () {
console.log(this.name);
},
// ✅ Bom: Abreviação ES6
introduce() {
console.log(this.name);
},
};
// 2. Usar Arrow Functions para callbacks
class Component {
constructor() {
this.name = 'Component';
}
mount() {
// ✅ Bom: Arrow Function preserva this
setTimeout(() => {
console.log(this.name);
}, 1000);
}
}
// 3. Usar funções regulares quando this dinâmico é necessário
const button = {
label: 'Click me',
// ✅ Bom: Precisa acessar o this do elemento DOM
handleClick: function () {
console.log(this); // elemento DOM button
},
};
❌ Práticas não recomendadas
// 1. Não usar Arrow Functions para métodos de objeto
const obj = {
name: 'Object',
// ❌ Ruim: this não aponta para obj
greet: () => {
console.log(this.name); // undefined
},
};
// 2. Não usar Arrow Functions como construtoras
// ❌ Ruim: Arrow Functions não podem ser construtoras
const Person = (name) => {
this.name = name; // Erro!
};
// 3. Não usar Arrow Functions quando precisa acessar arguments
// ❌ Ruim: Arrow Functions não têm arguments
const sum = () => {
console.log(arguments); // ReferenceError
};