https://pedromartins.com.pt/2021/02/18/thisem-javascript/

A natureza dinâmica da palavra reservada ´this´, é um dos conceitos em Javascript que mais problemas pode gerar, mesmo para quem já tem alguma familiaridade em programação – e este problema não é exclusivo da linguagem Javascript. Quem nunca encontrou resultados inesperados na execução de um script porque ´this´ fazia referência a algo que não o inicialmente previsto, que atire a primeira pedra.

A mehor analogia que encontro para compreender a utilização da palavra reservada ´this´ é considerá-la em tudo idêntica à utilização que fazemos dos pronomes na nossa gramática. Vejamos a seguinte frase:

A Rita Catita está a estudar, porque ela tem um exame na próxima semana.

Embora gramaticalmente correcto, ninguém no seu juízo perfeito constrói a frase desta forma:

A Rita Catita está a estudar, porque a Rita Catita tem um exame na próxima semana.

O pronome (ela) é utilizado como referência ao sujeito – neste caso , Margarida – e poupa-nos à sua repetição ao longo da frase. Na gramática da linguagem Javascript, o sujeito refere-se ao objecto que está a executar o código . Perdoem-me se a analogia não for a melhor!

Tentemos reproduzir o exemplo anterior em Javascript:

var student = {
  fName: 'Rita',
  lName: 'Catita',
  logName: function() {
    // 'this' como um pronome, referenciando o sujeito (student)
    console.log(this.fName, this.lName);
    // Repetindo o sujeito
    console.log(student.fName, student.lName);
  }
}

student.logName(); // Rita Catita  Rita Catita

Analisemos alguns exemplos da utilizacao de ‘this‘:

1. No contexto Global

Qualquer valor declarado no contexto global, pode ser acedido através da palavra reservada ‘this‘. No browser ‘this‘ refere-se ao objecto ‘window‘.
Vejamos:

var fName = 'Pedro';
var lName = 'Martins';
isDeveloper = true;

console.log(this.fName); // Pedro
console.log(window.fName); // Pedro

console.log(this.lName); // Martins
console.log(window.lName); // Martins

console.log(this.isDeveloper); // true
console.log(window.isDeveloper); // true

2. No contexto de uma Função

No contexto de uma função, o valor ‘this’ depende de onde a função é executada. Se a função é executada no contexto global, o valor de ‘this‘ será o objecto ‘window‘.

var name = 'Pedro Martins';

function getName() {
  var name = 'Rui Silva';
  return (this.name);
}

console.log(getName()); // Pedro Martins

No entanto, veremos mais abaixo que há casos em que podemos atribuir um valor em particular, utilizando os métodos call() and apply().

3. No contexto de um Objecto

Quando usado num objecto, o comportamento da palavra reservada ‘this‘ refere-se ao próprio objecto, como seria de esperar.

var name = 'Rui Silva';

var developer = {
  name: 'Pedro Martins',
  isDeveloper: true,
  getName: function() {
    return (this.name);
  }
}

console.log(developer.getName()); // Pedro Martins

Como acima referido, podemos atribuir a ‘this‘ um valor em particular, utilizando diferentes métodos. Analisemos então os métodos call(), apply() e bind().

call();

O método call(), aceita parâmetros opcionais. O primeiro parâmetro indica que será o objecto a que ‘this‘ fará referência. Se nenhum argumento for passado, será o objecto ‘window‘ a referência. Os restantes parâmetros correspondem aos parâmetros da função invocada, se ela os possuir.

var brand = 'Mini';
var make = 'Cooper S';

var car = {
  brand: 'Toyota',
  make: 'Avensis',
  getDetails: function() {
    return `${this.brand} ${this.make}`;
  }
}

var otherCar = {
  brand: 'Lexus',
  make: 'IS250'
} 

console.log(car.getDetails()); // Toyota Avensis

function logCar() {
  var brand = 'Mazda';
  var make = 'MX-5';
  console.log(`${this.brand} ${this.make}`);
}

function unlockCar(state) {
  if(state) {
    console.log(`${this.brand} ${this.make} is unlocked.`)
  } else {
    console.log(`${this.brand} ${this.make} is locked.`)
  }
}

logCar(); // Mini Cooper S
logCar.call(otherCar); // Lexus IS250
logCar.call(car); // Toyota Avensis

unlockCar.call(car, true); // Toyota Avensis is unlocked
unlockCar.call(this, false); // Mini Cooper S is locked
unlockCar.call(otherCar, true); // Lexus IS250 is unlocked

apply();

O método apply() é em tudo idêntico ao método call(). A principal diferença reside na forma como os argumentos opcionais são passados. Em vez de declarados explicitamente (como no método apply()), aqui são passados sob a forma de um array.

var brand = 'Mini';
var make = 'Cooper S';
var route = ['Lisboa', 'Faro'];

var car = {
  brand: 'Toyota',
  make: 'Avensis',
  getDetails: function() {
    return `${this.brand} ${this.make}`;
  }
}

var otherCar = {
  brand: 'Lexus',
  make: 'IS250'
} 

function logCar() {
  var brand = 'Mazda';
  var make = 'MX-5';
  console.log(`${this.brand} ${this.make}`);
}

function driveCar(from, to) {
  console.log(`Driving ${this.brand} ${this.make} from ${from} to ${to}.`);
}



logCar(); // Mini Cooper S
logCar.apply(otherCar); // Lexus IS250
logCar.apply(car); // Toyota Avensis

driveCar.apply(car, route); // Driving Toyota Avensis from Lisboa to Faro

var brand = 'Mini';
var make = 'Cooper S';
var route = ['Lisboa', 'Faro'];

var car = {
  brand: 'Toyota',
  make: 'Avensis',
  getDetails: function() {
    return `${this.brand} ${this.make}`;
  }
}

var otherCar = {
  brand: 'Lexus',
  make: 'IS250'
} 

function logCar() {
  var brand = 'Mazda';
  var make = 'MX-5';
  console.log(`${this.brand} ${this.make}`);
}

function driveCar(from, to) {
  console.log(`Driving ${this.brand} ${this.make} from ${from} to ${to}.`);
}



logCar(); // Mini Cooper S
logCar.apply(otherCar); // Lexus IS250
logCar.apply(car); // Toyota Avensis

driveCar.apply(car, route); // Driving Toyota Avensis from Lisboa to Faro

driveCar.apply(this, route); // Driving Mini Cooper S from Lisboa to Faro

bind();

O método bind(), à semelhança dos métodos anteriores, também altera o contexto ou a referência a que ‘this’ respeita. A grande diferença reside no facto de não executar a função mas antes criar uma nova função com o valor ‘this’ associado.

var car = {
  brand: 'Toyota',
  make: 'Avensis',
  getDetails: function() {
    console.log(`${this.brand} ${this.make}`);
  }
}

var otherCar = {
  brand: 'Lexus',
  make: 'IS250'
} 

/* Função invocada no objecto car */
car.getDetails(); // Toyota Avensis

/* Função invocada no contexto global onde não existem as propriedades brand e make */
var unboundDetails = car.getDetails;
unboundDetails(); // undefined undefined

/* Função invocada com o contexto do objecto otherCar */
var boundDetails = car.getDetails.bind(otherCar);
boundDetails(); // Lexus IS250

No contexto de uma Função Construtora

No contexto de uma função construtora, cada vez que instanciamos um novo objecto, ‘this‘ refere-se ao novo objecto criado.
Vejamos:

function Developer(name, lang) {
  this.name = name;
  this.favLang= lang;
  this.logDetails = function() {
    console.log(`O meu nome é ${this.name} e a minha linguagem de programação favorita é ${this.favLang}`);
  }
}

let me = new Developer('Pedro', 'Javascript');
me.logDetails(); // O meu nome é Pedro e a minha linguagem de programação favorita é Javascript

Os casos de estudo acima referidos são a base e um ponto de partida para melhor compreender o comportamento de ‘this’ em diferentes cenários. Num futuro post sobre ES6 voltarei a abordar o comportamento de ‘this’ em ‘arrow functions’.

Share: