Context vs Scope
Antes de entendermos os diferentes tipos de escopo, precisamos clarear o entendimento sobre a diferença entre contexto e escopo. Apesar de parecerem ter a mesma definição, não são a mesma coisa.
Toda função quando executada tem um contexto e um escopo associados. O escopo nada mais é do que a visibilidade das variáveis para o código. Enquanto podemos nos referir ao contexto como o
this
que está relacionado ao objeto que a função pertence."this" Context
O contexto é determinado pela forma com que a função é executada.
Quando uma função é executada como método de um objeto,
this
se refere ao objeto em que o método foi chamadoconst obj = { foo: function() { return this; } }; obj.foo() === obj; // Output: true
O mesmo princípio se aplica quando executamos uma função usando o operador
new
, para criar uma instância de um objeto. Dessa forma, this
se refere a recém criada instância.function foo() { alert(this); } const instance = new foo() // foo const anotherInstance = new foo() // foo console.log(instance === anotherInstance) // Output: false
Quando a função executada não está vinculada a um objeto ou construtor,
this
se refere ao objeto global. Porém, no strict mode, será undefined
.Function Scope
Antes da especificação ES6, da introdução dos statements
const
e let
, e do block scope, só existia uma forma de declarar variáveis: usando var
. Consequentemente apenas dois tipos de escopos: function scope e global scope. Uma variável declarada com
var
é function scoped, o que significa que só existirá dentro do escopo da função em que foi declarada.function fn() { var name = "John" console.log(name) // Output: John } console.log(name) // Output: name is not defined
A variável
name
existe apenas dentro da função fn
.Outros tipos de blocos como if e loops não são considerados como um escopo. Apenas funções.
if(true) { var name = "John" } console.log(name) // Output: "John"
A variável
name
está disponível fora do bloco if
em que foi declarada pois estão no mesmo escopo.Block Scope
Na especificação ES6,
let
e const
foram introduzidos como formas alternativas de se declarar variáveis e com eles, veio o block scope. No block scope, qualquer bloco de código é considerado um escopo.Uma função continuará sendo um escopo assim como quando usando
var
function fn() { const name = "John" console.log(name) // Output: John } console.log(name) // Output: name is not defined
A diferença é que outros blocos também são considerados como escopo
if(true) { const name = "John" } console.log(name) // Output: name is not defined
Lexical Scope
Escopo léxico permite que em um grupo de funções aninhadas, as funções internas tenham acesso às variáveis e recursos do seu escopo superior. Isso significa que essas funções internas estão vinculadas ao execution context - ver Call Stack, Execution Context and Heap - dos seus pais.
function father() { const name = "John" function child() { console.log("Father name", name) // Output: Father name John } }