The “closure” of the interviewer’s perspective

Closures are often difficult to answer, not because closures themselves are arcane, but because there are so many stories behind them to dig into. Those of you with more experience with interviews will notice that in most interview scenarios, the interviewer won’t ask you “What’s the closure?” instead, the interviewer will throw out a code snippet and ask, “What does this code do?” . When this happens, it’s a relief, because you’re running code in your head. Is the mildest and least painful way to look at closures. In response to this approach, we will also have problem sets for you to do in this section. But beyond that, I want you to draw attention to another way of asking the question — “How do you understand closures in JavaScript?” The problem is very differentiated. The characteristic of it is that everyone can answer a sentence or two, but only a few people can answer it well. And it often does not appear as an isolated problem, but as a “lead-in” to something deeper. When the interviewer asks you “how to understand”, he is a big probability is not want to hear you recite “closure is a function of XXXXXX”, but wanted to talk to you hit a JS languages, such as scope, the scope chain, the core of the knowledge, clever the interviewer, will also lead to variable ascension of opportunity and temporary additional topics, such as dead zone, the execution context What are the essential differences between different exceptions in JS? What is the lexical scope model? So understanding the closure code is just the first step to solving the closure problem. The crux of the matter is the story behind closures. Next, we will pick up the pieces and dismember these seemingly profound problems bit by bit to help you master the whole piece of knowledge veins involved in closures from the root.

Understand the scope implementation mechanism

As you know, the most basic capability of almost every programming language is the ability to store the value of a variable and allow us to access and modify its value. So once you have variables, where should you put them and how does the program find them? Does this require us to agree in advance on a set of rules for storing and accessing variables? This set of rules is what we call scope. More often, when we refer to scope, we refer to the area (which is more specific) that a variable, function, or identifier can be accessed under the constraints of this rule. To understand the scope implementation mechanism, we need to look at the JS compilation principle together (don’t run, this is not difficult). Let’s look at a simple declaration:

var name = 'xiuyan'
Copy the code

How do you think JS will understand this “sentence”? In our view, this is just a declaration statement. In the eyes of the JS engine, however, it contains two declarations:

  • Var name (compile time processing)
  • Name = ‘xiuyan’ (runtime processing)

What is compile time and what is runtime? Isn’t JS a “dynamic language” with no compile phase? In fact, JS also has a compilation phase, which differs from traditional languages in that JS does not finish the compilation work early, but executes it as it is compiled. Simply put, all JS snippets are compiled before they are executed, but the compilation process is very brief (perhaps a few subtlety or less) and then the code is executed. Returning to our statement, let’s look at what happens in the compile and execute phases:

  • Compilation stage: Here comes a guy called the compiler. The compiler looks through the current scope to see if there is already a guy named Name. If so, ignore the var name declaration and continue compiling. If not, add a name to the current scope. The compiler then generates code for the engine to run, and the program enters the execution phase
  • Execution stage: at this moment stage is we often hear the JS engine. When executing code, the JS engine will still search the current scope to see if there is a guy named name. If you can find it, all is well, I’ll assign it to you. If it can’t find it, it doesn’t get discouraged, it sticks its head out of the current scope to see if it’s “out there,” or “out of there.” If the name variable is still not found, the engine throws an exception.

One of the interesting things that comes out of this is our engine’s search process — what does poking your head out mean? What is “outside”? This leads us to a very important concept in JS scope – scope chain.

The scope covers the scope, and you have the scope chain

We now know that a scope is essentially a rule by which a program stores and accesses variables. In the last section, we talked about how scopes are implemented in the JS language. Now, let’s see how this set of rules actually works. In the JS world, there are already three scopes:

  • Global scope
  • Function scope
  • Block scope

Let’s use examples to remind you of these three domains:

  1. Global scope

A variable declared outside the top-level scope of any function is a global variable. Such a variable has a global scope:

var name = 'xiuyan'; // Variables in the global scope
// Function scope
function showName() {
    console.log(name);
}
// block scope
{
  name = 'BigBear'
}
showName(); / / output 'BigBear'
Copy the code

As we can see from the above example, global variables can be obtained in the global, function, and block scopes

  1. Function scope

A variable defined inside a function that has a function scope.

var name = 'xiuyan'; // Name is global
function showName(myName) {
  // myName is a local variable passed to showName
  console.log(myName);
}
function sayHello() {
  // Hello is defined as a local scope variable
  var helloString = 'hello everyone';
  console.log(helloString);
}
showName(name); / / output 'xiuyan'
sayHello(); // Print 'hello everyone'
console.log(myName); // Throw an error: myName is not defined in global scope
console.log(helloString); // Throws an error: Hello is undefined in global scope
{
  console.log(helloString, myName) // Throw an error
}
Copy the code

In this case, myName and Hello are variables defined inside the function, so they are “locked down” and scoped only inside the function. They are not accessible in the global or block scope.

  1. Block scope

As ES6 begins, we have two new keywords for declaring variables: let and const. The variables defined by these two keywords, if enclosed by a curly brace {}, are a code block, and the variables enclosed by curly braces form a block scope:

{
  let a = 1;
  console(a);
}
 console(a); / / an error
function showA() {
  console.log(a) / / an error
}
Copy the code

In this example, we can see that the variable in the block scope is not accessible as long as it is outside the code block defined by itself. This is similar to function scopes — they only work “on their own turf”, so they are collectively referred to as “local scopes”.

The scope chain

OK, now that we have completed our overview of scope types, we are back on the scope chain. In our actual development, we usually use more than one scope. Nesting of scopes occurs when a block or function is nested within another block or function. Like this:

function addA(a) {
  console.log(a + b)
  console.log(c) / / an error
}
var b = 1
addA(2) / / 3
Copy the code

In this example, there are two scopes: the functional scope of addA and the global scope. Their relationship is shown as follows:

When we try to access variable B in addA, we cannot find it at first, considering that there is no definition of b and C in the function scope. What do you do to find B and C? “Stick your head out”, right? Stick your head out, go to the upper scope (global scope), find B, then you can use it directly; Failed to find c, and global scope has no upper scope. This is the process we described in the “execution phase” above. In this search process, a chain of scopes is formed as layers of scopes progress. In the example above, the scope chain is shorter:

Understand the closure

Let’s look at another example:

function addABC(){
  var a = 1,b = 2;
  
  function add(){
    return a+b+c;
  }
  return add;
}
var c = 3
var globalAdd = addABC()
console.log(globalAdd()) / / 6
Copy the code

In this example, scope nesting is shown as follows:

The scope chain relationship is shown as follows:

So add, it’s nested inside addABC, and if you want to find a, B, and C, it has to go up to addABC, right? Like a, B, and CFunction, but it is neither a function parameter nor a local variable of the function, but a variable that is not in the current scope, it is a free variable relative to the current scope. A function like Add that refers to a free variable is called a closure.

Closures, if you understand them to this extent, will not be a disadvantage in your interview. You can say that the closure definition for this version, which you will answer in your interview, is 100 points. But if you want to get into a big factory and a good team, then 100 might not be enough, you need 120. 120, and we’ll spend a whole section on that in the next section.

LHS, RHS — What is the interviewer asking?

In the interview process, some interviewers who have high expectations of technical depth, in order to test the “root” of everyone (not to be sure that part of the purpose is to fake x :), may accidentally throw out LHS, RHS and other “advanced” terms about scope and variable access. And just in case anyone gets hurt, here are the two guys for a walk:

LHS and RHS are the two ways in which the engine queries variables while executing code. Where L and R mean Left and Right respectively. The “left” and “right” are relative to the assignment. When a variable appears on the left side of the assignment, the LHS operation is performed, and the RHS operation is performed on the right side:

In this case, the name variable appears on the left side of the assignment, which is LHS. LHS means a variable is assigned or written to memory. It emphasizes the act of writing, so an LHS query looks at the “home” (corresponding memory space) of the variable.

In this case, there is an assignment on the first line, but name is on the right side of the operation, so RHS; There is no assignment in the second line, so name can be understood as not appearing on the left side of the assignment. In this case, we also consider the query for name to be RHS. RHS stands for variable lookup or reading from memory. It emphasizes the action of reading, which queries the contents of a variable. For LHS and RHS, if you can understand the two examples above and tell how they work, it will be sufficient. Next, we will enter the study of lexical scope