The simplest explanation of JS scope and closure
As a more functional programming styled language, JavaScript has a simpler variable scope than some other object-oriented styled language, like C# and Java.
1. Variable scope
Before ES6, we only have 2 scopes for variables: global scope and function scope. In ES6, we also have an extra scope: block scope.
const globalVar = 'globalVar'; // global scope
function func() {
const funcVarA = 'funcVarA'; // func scope
console.log(`${globalVar} ${funcVarA}`);
}
console.log(funcVarA); // undefined
console.log(result); // undefined
func(); // 'globalVar funcVarA'
Very simple, right? It's similar with most other language:
- The function scope variables can't be accessed out of the function, but a global variable can be used by any functions
- in a nested functions structure, the child function has the access of variables in its parent function(s), but a parent function can't access the variables in any of its children functions.
Note: when
func
is invoked, thefuncVarA
will be collected from memory.
The question is, if my code is like this:
function func() {
const funcVarA = [1,2,3]; // func scope
return function childFunc() {
let result = 0; // func scope
for(let i=0; i < funcVarA.length; i++) { // i is in block scope
result += funcVarA[i];
}
return result;
}
}
const sum = func(); // func is already invoked
const result = sum();
console.log(result); // 6
My question is, the func
is already invoked and return the function childFunc
to sum
, and when sum
is invoked, we can get the correct result 6, which means in childFunc
still can access the funcVarA
from func
even func
was executed before.
This JavaScript feature is called CLOSURE.
2. Closure
Don't be scared be this naming, it's very simple: it's a feature of JavaScript that when you invoke a function, you can still access its outside-scope variables when it is defined, like a ***"closed bundle"***.
Free variable: a variable that is used in a function but is not local or parameters of that function.
Usually, closure will happen for two scenarios:
-
function is returned as a result. We can simplify above code like this:
function create() { const a = 100; return function () { console.log(a); } } const fn = create(); const a = 200; fn();
Q: What's the printing result?
A: 100.
-
function is passed as a parameter:
function print(fn) { const a = 200; fn(); } const a = 100; function fn() { console.log(a) } print(fn);
Q: What's the printing result?
A: 100.
The conclusion is very obvious: a
is a free variable, with closure feature, complier will follow the scope chain to search a
in the place where the function is defined, NOT where the function is invoked.