Palavra reservada "this"

"this" é uma referência de contexto

A palavra reservada this é uma referência de contexto.
Na maioria dos casos, o valor de this é determinado por como uma função é chamada (runtime binding).
Não pode ser definido por atribuição durante a execução e pode ser diferente cada vez que a função é chamada.
Valor: Uma propriedade de um contexto de execução (global, função ou eval) que, no modo não estrito, é sempre uma referência a um objeto e no modo estrito pode ser qualquer valor.

Tabela de Contexto X Referências

Contexto Referência
Método de um objeto Próprio objeto dono do método
Sozinha Contexto global (em navegadores, window)
Função Contexto global
Evento Elemento que recebeu o evento
Método de um Objeto

                            const person = {
                                id: 1,
                                firstName: 'John',
                                lastName: 'Doe',
                                age: 30,
                                fullName: function() {
                                    return this.firstName + ' ' + this.lastName;
                                },
                                getId: function() {
                                    return this.id;
                                }
                            }
                        
Navegador
No contexto de execução global (fora de qualquer função), this refere-se ao objeto global, seja em modo estrito ou não.
In web browsers, the window object is also the global object:

                            console.log(this);  // Window {window: Window, ...}

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

                            a = 37;
                            console.log(window.a); // 37

                            this.b = "MDN";
                            console.log(window.b)  // "MDN"
                            console.log(b)         // "MDN"

                            var name = 'teste';
                            console.log(this.name); // teste
                        
Contexto de Função
Dentro de uma função, o valor de this depende de como a função é chamada.

Exemplo, this refere-se ao objeto global, porque o valor de this está definido pela chamada:

                            // Modo non-strict 
                                function f1() {
                                    return this;
                                }
                                
                                // In a browser:
                                f1() === window; // true
                                
                                // In Node:
                                f1() === globalThis; // true 

                                // Node: Object [global]
                                // Browser: Window
                                (function(){ console.log(this)})(); 

                            // Modo strict
                                function f2() {
                                    'use strict';
                                    return this;
                                }
                                f2(); // undefined
                        
No modo strict, o valor de this é undefined, pois a função f2() foi chamada diretamente e não como método ou propriedade de um objeto (ex: window.f2()).
Para definir o valor de this para um valor específico ao chamar uma função, use call() ou apply().

Exemplo, this refere-se ao objeto chamador, contexto de execução da função:

                            let user = {
                                name: "Heviane",
                                getName: function() { console.log( this.name ); }
                            };
                            user.getName(); // Heviane
                        
Contexto de Arrow Functions
this retém o valor do contexto léxico envolvente this.
No código global, será definido para o objeto global:

                            var globalObject = this;
                            var foo = (() => this);
                            console.log(foo() === globalObject); // true
                        
DOM Event Handler
Inline Event Handler
When the code is called from an inline on-event handler, its this is set to the DOM element on which the listener is placed: Quando o código é chamado de um manipulador de evento embutido, this é definido para o elemento DOM no qual o ouvinte é colocado.

O alerta acima mostra button.

Na função interna, this não está definida, então retorna o objeto global, que no navegador é window (ou seja, o objeto padrão no modo no-strict onde this não está definido pelo chamador).
// Evento de um elemento...Falta testar...

                            const button = document.querySelector('button');

                            button.addEventListener('click', function() {
                                console.log(this); // button
                            });
                        
Classes
Assim como em regular functions, o valor do this é definido pela chamada (contexto de execução da classe).
Às vezes é útil substituir esse comportamento para que this dentro da classe sempre se refira à instância da classe. Para fazer isso use o método bind() no construtor da classe.

                            class Car {
                                constructor() {
                                    // Bind sayBye but not sayHi to show the difference
                                    this.sayBye = this.sayBye.bind(this);
                                }
                                sayHi() {
                                    console.log(`Hello from ${this.name}`);
                                }
                                sayBye() {
                                    console.log(`Bye from ${this.name}`);
                                }
                                get name() {
                                    return 'Ferrari';
                                }
                            }
                                
                            class Bird {
                                get name() {
                                    return 'Tweety';
                                }
                            }
                                
                            const car = new Car();
                            const bird = new Bird();
                            
                            // The value of 'this' in methods depends on their caller
                            car.sayHi(); // Hello from Ferrari
                            bird.sayHi = car.sayHi;
                            bird.sayHi(); // Hello from Tweety
                            
                            // For bound methods, 'this' doesn't depend on the caller
                            bird.sayBye = car.sayBye;
                            bird.sayBye();  // Bye from Ferrari                              
                        
Classes sempre são em modo strict. Chamar métodos com um this indefinido irá gerar um erro. Nota: Você sempre pode obter facilmente o objeto global usando a globalThis propriedade global, independentemente do contexto atual em que seu código está sendo executado.

                        // Browser
                        console.log(globalThis); // Window {window: Window, ...}

                        // Terminal
                        console.log(globalThis); // Object [global]
                    

Métodos nativos para manipular o valor de "this"

Método Call() das Funções - Function.prototype.call()

Chama uma função com um this valor e uma lista de argumentos individuais nomeados.
Sintaxe: fun.call(thisArg, arg1, ... , argN)

                            const person = { name: 'Miguel' };
                            const animal = { name: 'Floquinho' };

                            function getSomething(){
                                console.log(this.name);
                            }

                            getSomething.call(person); // Miguel
                            getSomething.call(animal); // Floquinho
                        
Exemplo com parâmetros

                                const obj = {
                                    num1: 1,
                                    num2: 2
                                }
                                
                                function sum(a, b){
                                    return this.num1 + this.num2 + a + b;
                                }
                                
                                sum.call(obj, 3, 4); // 10
                            
Se o primeiro argumento this não for passado, o valor de this será vinculado ao objeto global, exceto se estiver no modo strict, aí irá retornar undefined.

Método Apply() das Funções - Function.prototype.apply()

Chama uma função com um this valor e um array de argumentos.
Sintaxe: fun.apply(thisArg, [argsArray])

                            const person = { name: 'Miguel' };
                            const animal = { name: 'Floquinho' };

                            function getSomething(){
                                console.log(this.name);
                            }

                            getSomething.apply(person); // Miguel
                            getSomething.apply(animal); // Floquinho
                        
Exemplo passando parâmetros

                                const obj = {
                                    num1: 1,
                                    num2: 2
                                }
                                function sum(a, b){
                                    return this.num1 + this.num2 + a + b;
                                }
                                sum.apply(obj, [3, 4]); // 10
                            

Método Bind() das Funções - Function.prototype.bind()

Clona a estrutura da função onde é chamada e aplica o valor do objeto passado como parâmetro.
Cria uma nova função com o mesmo corpo e escopo da função clonada, mas onde this ocorre na função original, na nova função ela é permanentemente vinculada ao primeiro argumento de bind(), independentemente de como a função está sendo usada.

                            cconst returnNames = function (){
                                return this.name;
                            }
                            let maria = returnNames.bind({name: 'Maria'});
                            let ana = maria.bind({name: 'Ana'}); // *** bind só funciona uma vez! ***
                            
                            console.log( maria() ); // Maria
                            console.log( ana() );   // Maria
                        
A variável "maria" agora é uma função que retorna o valor "Maria".

                            function f() {
                                return this.a;
                            }
                            
                            var g = f.bind({a: 'azerty'});
                            console.log(g()); // azerty
                            
                            var h = g.bind({a: 'yoo'}); // *** bind só funciona uma vez! ***
                            console.log(h()); // azerty
                            
                            var o = {a: 37, f: f, g: g, h: h};
                            console.log(o.a, o.f(), o.g(), o.h()); // 37, 37, azerty azerty                              
                        

                            this.x = 9; //this aqui se refere ao objeto global "window" do navegador
                            var module = {
                                x: 81,
                                getX: function() { return this.x; }
                            };

                            console.log( module.getX() ); // 81

                            var retrieveX = module.getX;
                            console.log( retrieveX() ); // retorna 9 - a função foi invocada no escopo global

                            // Criando uma nova função com 'this' vinculada ao módulo
                            // Novatos podem confundir a variável x global com prop x do módulo
                            var boundGetX = retrieveX.bind(module);
                            console.log( boundGetX() ); // 81
                        
Nota: A sintaxe de apply() e call() são quase idênticas, a diferença é que call() aceita uma lista de argumentos, enquanto apply() aceita um array de argumentos.

Diferenças de "this" entre Classes e Funções