javascript – Using this in functions: How does context work?

Question:

There is one thing that I do not understand and it is in this case: the operation of this .

this always point to the object containing the function and call the this from the function. In the example I put below, my second this points to the object I have created, but why does the first this point to the button that is an html element?

onclick is an event, boton.onclick = function() Do I have to see it as if onclick was a property that I give to the button object? Should I interpret it like this? Because come on, button is an html element, I mess with how to interpret this, or is this pointing to events not only objects since the event is onclick ?

var boton = document.getElementsByTagName('button')[0];
  boton.onclick = function(){
    console.log(this);

    let XMLrequest = new XMLHttpRequest();
    XMLrequest.onreadystatechange = function() {
      console.log(this);
    }
  }
<html>
<head>
  <title>JSON</title>
  <meta charset="utf-8">


</head>
<body>
  <button>click</button>

</body>
</html>

Answer:

If you want a very simplified version:

this points to the context. In event handlers, this is the element that received the event (the button, the div, the link …). If you call a function as if it were a method, this is the object. But that the function is declared as a method does not ensure that it is called as such.

If you want a longer and "didactic" version, with examples:

The behavior of this is a bit special in Javascript: It indicates the context in which you are executing the code. If you are in the global context (you are not inside any function), this is window . If you call a function simply using its name, the context is preserved:

console.log(this===window);
function test() {
  console.log(this===window);
  if (this!==window) {
    console.log(this);
  }
}

test();

But in strict mode, the context when calling a function using its name is undefined :

'use strict';

console.log(this===window);
    
function test() {
  console.log(this===window);
  if (this!==window) {
    console.log(this);
  }
}

test();

But if that function is an attribute of an object, then this becomes that object:

 function test() { console.log('Es window?:' + (this===window)); if (this!==window) { console.log('Es obj?:' + (this===obj)); } } console.log('Modo no estricto'); var obj={ prueba: 'hola' } test(); console.log('Como método'); obj.metodo=test; obj.metodo();

So when we define a callback function we can have problems if we don't know what the context is where the function is executed:

 class Contador { constructor() { this.count=0; } incrementar() { this.count++; } } let c= new Contador() c.incrementar(); console.log(c.count) let funcionInc=c.incrementar; //falla porque el contexto no es un objeto Contador funcionInc();

Suppose we have an event that we want to deal with with our counter object:

class Contador {

  constructor() {
    this.count=0;
  }
  
  incrementar() {
    if (this.count !== undefined) {
      this.count++;
    } else {
      console.log('No hay count para incrementar!');
    }
  }
}

let c= new Contador();

$('button').on('click',c.incrementar);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Click</button>

There are several solutions to this problem:

One is to "wrap" the execution with another function:

class Contador {

  constructor() {
    this.count=0;
  }
  
  incrementar() {
    if (this.count !== undefined) {
      this.count++;
      console.log(this.count);
    } else {
      console.log('No hay count para incrementar!');
    }
  }
}

let c= new Contador();

$('button').on('click',function () {c.incrementar();});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Click</button>

And another solution is to use bind() :

class Contador {

  constructor() {
    this.count=0;
  }
  
  incrementar() {
    if (this.count !== undefined) {
      this.count++;
      console.log(this.count);
    } else {
      console.log('No hay count para incrementar!');
    }
  }
}

let c= new Contador();
//con bind fijamos el contexto
let funcion=c.incrementar.bind(c);

$('button').on('click',funcion);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Click</button>

Arrow functions are a bit special, because they assign the context when they are defined, not when they are executed:

class Contador {

  constructor() {
    this.count=0;
    this.incrementar= () => { 
      this.count++;
      console.log(this.count);  
    }
  }
}

let c= new Contador();

//no hace falta bind, el contexto se fijó al declarar la función
$('button').on('click',c.incrementar);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Click</button>
Scroll to Top