Scope
There are just three different scopes in your code. One is the global scope all variables you declare in and the other two are here:
var SQUAREPLUSOTHER = x => y => ((x*x) + y + z);
There are two function scopes, one containing the variable x and the other function scope containing the variable y. It might be easier to see if you replace the arrow function with functions:
// global scope
function SQUAREPLUSOTHER(x) {
// function scope containing x
return function(y) {
// function scope containing y
return (x*x) + y + z;
};
}
How scoping works is actually quite simple:
Every { starts a new scope and its counter } closes it, variables declared inside it with let and const (or var but thats slightly more complicated) are part of the scope. If it is the scope of a function, the parameters (e.g. x and y) are part of that scope.
From the code, you can access the all variables that are in the current scope or in its parent scope, if there are multiple with the same name you get the most inner one. Therefore x inside of the SQUAREPLUSOTHER function refers to the variable of the SQUAREPLUSOTHER scope, while for code outside of it x is the global variable and the functions variable can't be accessed.
The Scope of a variable does not change at runtime, you can always directly see which scope a variable belongs tp by looking at the surrounding { .. }.
Now different variables in different scopes have to hold values at runtime, thats where we get to:
the environment record
When you call a function, the JavaScript engine creates a new "EnvironmentRecord" (which is like an internal object) that contains all the variables of the function you call, e.g. in this case:
function test(a) {
let b;
}
Then if you call that function (test(1)) a new environment record gets created that contains a and b. Now the code inside the function gets run, and every variable is looked up in it. If there are two functions nested into another, calling the inner function will create an environment record that holds a reference to the outer one:
function test(a) {
function test2(b) {
}
test2(5);
}
Now calling test(1) will create an record where a is 1. If the engine then executes the second call (test2(5)) it creates another record containing b being 5, and that holds a reference to the record containing a. Now if you use a inside test2, the engine will look it up in the current environment record, won't find it, and then look it up in the parent where it finds a being 1.
closure
Usually those records get deleted when the execution reaches the }, however if there is another record that got the current record as a parent, it won't get deleted. It will exist until all child records got deleted, then it will also remove the parent. This behaviour (variables live longer because they can be accessed from an inner functions body) is called a closure.