Palavra-chave this - JavaScript

O comportamento da palavra-chave this depende do contexto em que ela é utilizada e se é usado o modo estrito ou não.

Contexto Global e objeto globalThis

No contexto global, this refere-se ao objeto global. O que é o "objeto global" no JavaScript? Isso depende do ambiente em que o código é executado. Em um navegador web, this representa o objeto window, que representa a janela do navegador. Em um ambiente Node.js, this representa o objeto global. Para web workers, this representa o objeto self.

Por exemplo, no navegador, ao executar o seguinte código:

console.log(this);

Você verá algo semelhante a:

Window {window: Window, self: Window, document: document, name: "", location: Location, …}

O padrão ES2020 introduziu o objeto globalThis, que permite se referir ao contexto global, independentemente do ambiente ou situação em que o código é executado:

console.log(globalThis);

Contexto de Função

Dentro de uma função, this refere-se ao contexto externo. Para funções definidas no contexto global, isso se refere ao objeto globalThis. Por exemplo:

function foo() {
    var bar = "local";
    console.log(this.bar);
}

var bar = "global";
foo();  // global

Se não usássemos this, a referência seria para a variável local definida dentro da função:

function foo() {
    var bar = "local";
    console.log(bar);
}

var bar = "global";
foo();  // local

No entanto, se estivermos no modo estrito, this seria undefined:

"use strict";
function foo() {
    var bar = "local";
    console.log(this.bar);
}

var bar = "global";
foo();  // erro: this = undefined

Contexto de Objeto

No contexto de um objeto, incluindo seus métodos, a palavra-chave this refere-se ao próprio objeto:

const obj = {
    bar: "object",
    foo: function() {
        console.log(this.bar);
    }
}
var bar = "global";
obj.foo();  // object

Definição Dinâmica de Contexto

Em JavaScript, a palavra-chave this dentro de uma função é determinada pelo contexto em que a função é chamada, não onde é definida. Vamos explorar um exemplo mais complexo:

function foo() {
  var bar = "foo_bar";
  console.log(this.bar);
}

const obj1 = { bar: "obj1_bar", foo: foo };
const obj2 = { bar: "obj2_bar", foo: foo };

var bar = "global_bar";

foo();        //  "global_bar"
obj1.foo();   //  "obj1_bar"
obj2.foo();   //  "obj2_bar"

Neste exemplo, a variável global bar é definida com o valor "global_bar". A função foo, por outro lado, define uma variável local bar com "foo_bar". Contudo, o valor de bar que é impresso pela função foo é determinado pelo contexto externo, ou seja, o contexto global por padrão, portanto, imprime a variável global "global_bar" porque o script é executado em modo não estrito.

No caso de objetos, eles definem seu próprio contexto que inclui a propriedade bar. Assim, quando o método foo é chamado como um método de obj1 ou obj2, o contexto externo é o dos objetos, respectivamente.

Considere outra situação para ilustrar possíveis confusões:

var bar = "global_bar";

const obj1 = {
    bar: "obj1_bar",
    foo: function() {
        console.log(this.bar);  // "obj1_bar"
    {
{;
const obj2 = { bar: "obj2_bar", foo: obj1.foo }; // "obj2_bar"

const foo = obj1.foo;   // Refere-se à função foo definida em obj1

obj1.foo(); // "obj1_bar"
obj2.foo(); // "obj2_bar"
foo();      // "global_bar"
  

Na situação discutida, a função foo dentro de obj1 é definida como segue:

const obj1 = {
  bar: "obj1_bar",
  foo: function() {
      console.log(this.bar);  // "obj1_bar"
  }
};

Aqui, a função foo usa this.bar, que pega o valor de bar do contexto externo de obj1, isto é, "obj1_bar".

O objeto obj2 utiliza a função foo de obj1:

const obj2 = {bar: "obj2_bar", foo: obj1.foo};

Quando obj1.foo é chamada como parte de obj2, ela procura this.bar no contexto externo, que agora é obj2. Por isso, this.bar será "obj2_bar".

O mesmo acontece quando foo é atribuída a uma variável global e chamada:

const foo = obj1.foo;

Neste caso, a função busca this.bar no contexto global, onde a variável bar é "global_bar".

Contexto em Funções Aninhadas

Quando uma função é chamada dentro de outra função, o contexto da função interna será o mesmo da função externa, a menos que seja explicitamente modificado:

var bar = "global bar";

function foo() {
    var bar = "foo bar";
    function moo() {
        console.log(this.bar);
    }
    moo();
}

foo();  // "global bar"

A função moo usa this.bar que, apesar de moo ser chamada dentro de foo, é determinado pelo contexto de chamada de foo, que é o contexto global, resultando em "global bar".

Uso de call() e apply()

Esses métodos permitem que você especifique explicitamente o contexto para o qual this deve se referir:

function foo() {
  console.log(this.bar);
}

var obj = {bar: "obj_bar"}
var bar = "global_bar";
foo();            // "global_bar"
foo.apply(obj);   // "obj_bar"
foo.call(obj);    // "obj_bar"

Método bind

O método bind é usado para criar uma nova função que, quando chamada, terá seu this definido permanentemente para um valor específico, independente de como a função é chamada:

function foo() {
  console.log(this.bar);
}

const obj = {bar: "object"}
var bar = "global";
foo();  // "global"
const func = foo.bind(obj);
func(); // "object"

this e Funções Arrow

Nas funções arrow, o this é determinado pelo contexto de encapsulamento, e não pelo ambiente em que a função arrow é definida. Vejamos o seguinte exemplo:

const person = {
  name: "Tom",
  say: () => console.log(`Meu nome é ${this.name}`)
};
person.say();   //  "Meu nome é "

Neste exemplo, a função arrow say() tenta acessar a propriedade this.name, mas o this aqui refere-se ao objeto global (objeto window no navegador), pois funções arrow não têm seu próprio this. Como não há uma variável global name definida, o resultado impresso é apenas.

Meu nome é 

Alteremos um pouco o exemplo:

const person = {
  name: "Tom",
  hello() {
      console.log("Olá");
      let say = () => console.log(`Meu nome é ${this.name}`);
      say();
  }
};
person.hello();

Agora, a função arrow say está definida dentro do método hello(). O this para este método refere-se ao objeto person, onde o método está definido. Assim, em say, o this também se refere ao objeto person, e this.name acessa a propriedade name deste objeto. O resultado da execução será:

Olá
Meu nome é Tom

Apesar de as funções arrow adicionarem algumas complicações no trabalho com this, elas também podem resolver várias questões. Considere o seguinte código:

const school = {
  title: "Oxford",
  courses: ["JavaScript", "TypeScript", "Java", "Go"],
  printCourses() {
      this.courses.forEach(function(course) {
          console.log(this.title, course);
      });
  }
};
school.printCourses();

A função printCourses percorre todos os cursos e tenta prefixar cada um com o valor da propriedade title. No entanto, o resultado impresso será:

undefined "JavaScript"
undefined "TypeScript"
undefined "Java"
undefined "Go"

O valor de this.title é undefined porque o this dentro da função passada para forEach é o contexto global, e não o objeto school.

Uma solução é capturar this em uma variável externa:

const school = {
  title: "Oxford",
  courses: ["JavaScript", "TypeScript", "Java", "Go"],
  printCourses() {
      const that = this;
      this.courses.forEach(function(course) {
          console.log(that.title, course);
      });
  }
};
school.printCourses();

Funções arrow oferecem uma abordagem mais elegante:

const school = {
  title: "Oxford",
  courses: ["JavaScript", "TypeScript", "Java", "Go"],
  printCourses() {
      this.courses.forEach((course) => console.log(this.title, course));
  }
};
school.printCourses();

Com funções arrow, o contexto para a função é sempre o do objeto school, eliminando a necessidade de variáveis adicionais para passar o contexto.

Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com