As we said, scopes are essentially a set of rules. If the last section, we are the content of this “rule”, from beginning to end to sort out clearly for you. So what we’re going to talk about in this video is the cause of this rule. This section is relatively advanced content, students according to their current situation to choose. If you are currently in the entry/primary stage and do not have strong desire to be promoted to a large factory/medium or senior engineer, you are advised to read the next paper “The Application and Analysis of Closures” in the limited time, which will have a higher return on investment ratio.

Lexical scope and dynamic scope

I believe many of you may have been confused by the title – scope is scope, “lexical”, “dynamic” these strange prefixes are what? In fact, when we talk about the concept of “scope” in the context of the JavaScript language, we really don’t need to distinguish between “lexical” and “dynamic” because our JS scope follows the lexical scope model. Don’t panic when an interviewer throws out the term “lexical scope”, which refers to the JS scope you’re most familiar with. At the language level, however, there are two main working models for scopes:

  • Lexical scope: also known as static scope. This is the most common type of scope model and the focus of our study
  • Dynamic scoping: Relatively unfashionable, but some languages do adopt dynamic scoping: Bash scripts, Perl, etc

To understand lexical scope itself, we have to look at it outside of the JS box and put it in the context of its opposite, dynamic scope. To make both concepts more intuitive, let’s go straight to a piece of code:

var name = 'xiuyan';
function showName() {
    console.log(name);
}
function changeName() {
    var name = 'BigBear';
    showName();
}
changeName();
Copy the code

This is JS code, and based on what we learned about JS scope in the previous section, it is easy to say that the result is “Xiuyan”. This is because JS takes a lexical (static) scope, and the code runs through this variable location process:

  1. Look for the local variable name in the function scope of the showName function
  2. The value of name is xiuyan, so the result will print Xiuyan.

At this point, its scope relationship is shown as follows:

The run-time scope chain is as follows:

  • Here we define the scope as we write it (in the case of function definition, block scope as well as code block definition), depending on where you write it. The scope delineated like this follows the lexical scope model.

So what is dynamic scope? With dynamic scoping, the same piece of code will do the following:

  1. Look for the local variable name in the function scope of the showName function
  2. Finding no showName, I continue to look for name along the function call stack where showName was called. Now let’s see where it goes. Did you find your way into changeName? Well, changeName has a name, so that name will be referenced in showName.

At this point, its scope chain relationship is shown as follows:

The difference between lexical scope and dynamic scope is when the scope is demarcated:

  • Lexical scope: When code is written, the scope chain extends outward along its defined location
  • Dynamic scope: When the code is partitioned at runtime, the scope chain extends out along its call stack

If you are unfamiliar or uncomfortable with the concept of the “call stack”, don’t worry now, we will go into more depth and detail in the section “Understanding JS Context and call stack”.

Modify the lexical scope

In relatively advanced front-end interviews, interviewers sometimes ask questions like: How do you “cheat” the lexical scope? Don’t be fooled by the fancy word “cheat,” which means change. When the interviewer asks you how to change your scope, he or she is not really expecting you to change the scope rules while writing code (which often costs performance), but is trying to figure out how well you know lexical scope. How to understand the action of “modify”? JS follows the lexical scope model is a foregone conclusion, can I change it to dynamic scope? Don’t say it. It’s good. Isn’t your JS scope only partitioned at writing time? So I have to change the scope of your partition during the run – who is so cool? Let’s have eval and with:

  • Eval changes to a scope

Let’s start by reviewing the use of the eval function:

function showName(str) {
  eval(str)
  console.log(name)
}
var name = 'xiuyan'
var str = 'var name = "BigBear"'
showName(str) The BigBear / / output
Copy the code

As you know, the input to eval is a string. When eval takes a string as an entry, it treats the contents of the string as a piece of JS code (whether or not it is a piece of JS code) and inserts it into the position where it was called. So in the above example, the showName function “transformed” by eval looks like this:

function showName(str) {
  var name = 'BigBear'
  console.log(name)
}
Copy the code

At this point, when we tried to output name, the name in the function scope had been modified by the line passed in to Eval, so the value of name in the function scope changed from ‘xiuyan’ to ‘BigBear’ (see the change eval made in the figure below). This change does occur only after eval (STR) is executed — eval changes the scope at runtime, successfully “modifying” the scope that was delimited at writing time under the constraints of the lexical scope rules.

  • With changes to scope

With may be a little stranger to you than Eval. It helps us to be lazy when we don’t want to prefix an object name repeatedly:

var me = {
  name: 'xiuyan'.career: 'coder'.hobbies: ['coding'.'footbal']}// If we wanted to print a variable in the object me, we might do this without with:
console.log(me.name)
console.log(me.career)
console.log(me.hobbies)
// But with saves time writing prefixes
with(me) {
  console.log(name)
  console.log(career)
  console.log(hobbies)
}
Copy the code

Yes, with is a lazy way to refer to multiple attributes within an object. Why does with “change” the lexical scope? Let’s look at another example:

function changeName(person) {
  with(person) {
    name = 'BigBear'}}var me = {
  name: 'xiuyan'.career: 'coder'.hobbies: ['coding'.'footbal']}var you = {
  career: 'product manager'
}
changeName(me)
changeName(you)
console.log(name) / / output 'BigBear'
Copy the code

We were surprised to find that after executing changeName twice, we had a global variable named! That’s with. With creates a new scope out of thin air. For example, the first time you execute changeName, it looks like this:

If we substitute the ability to create a new scope with with into the two changeName executions, it’s not hard to see why there is an extra global name. Here’s what happened:

  • The first changeName call, with, creates a new scope for the me object, allowing direct access to name, career, hobbies, and other object properties in this scope. The process is what we have in the picture above. So far so good.
  • The second changeName call, with, also creates a new scope for the you object, allowing direct access to the career object property in this scope (figure below).

It turns out we’re trying to access name — a variable that doesn’t exist in the current scope. So what happens? Note that with only changes the scope of the “create” action. When the scope is created, its query rules still follow the query rules of our lexical scope, so it instinctively “pokes its head out” to its upper scope, the global scope, to find the name, and still can’t find it (the scope chain relationship is shown below).

Note that we are in non-strict mode. In non-strict mode, the system will automatically create a name in the global scope for you, even if no name is found in the global scope. So name = ‘BigBear’ is executed smoothly, and the global variable name appears ~.

It all came out. Here’s a quick summary of how with changes scope: It creates a new scope in place. This set of variables is actually the set of properties of the target object passed in with. Because the “create” action happens after the with code has actually been executed, the new scope is actually added at run time, and with thus implements the modification of the scope demarcated at writing time. It is important to note that “change” simply describes the action of “create” — the new scope created. So its scoped query mechanism still follows the lexical scoped model.

Tips: Don’t write code with and eval

Keep your head above water: We mention with and Eval here only to broaden your knowledge and make sure you don’t get caught up in the blind side of an interview, not to suggest the use of with and Eval. In fact, with and Eval have long been the bane of JS programmers’ minds because of their annoying side effects (such as a drag on language performance, global variables that “pop up” above us, etc.). No one actually uses it in the code, and I highly recommend not using it. During the interview, if the interviewer tries to ask a question such as “Tell me about your application of with and eval in a real project”, simply answer “I don’t write code with and eval”. Don’t worry about asking questions. Normal interviewers don’t ask questions. :)).

Dynamic scope

What languages use dynamic scopes? Bash is dynamically scoped, so we can create a bash file and put the following code in it:

#! /bin/bash

name=1
function showName () {
    echo $name;
}
function changeName () {
  local name=2;
  showName;
}
changeName
Copy the code

ShowName = 1; showName = 1; showName = 1; showName = 1; showName = 1; So we looked it up in the changeName function, and luckily we found it. And then it prints 2

classified

Most modern programming languages use static scoping rules, such as C/C++, C#, Python, Java, JavaScript…

Conversely, variables that are dynamically scoped are called dynamic variables. As long as the program is executing the code segment that defines a dynamic variable, the variable exists for that time; The variable disappears at the end of the code section. This means that if you have a function f that calls function G, then when you execute g, all local variables in f will be accessed by G. In the statically scoped case, g does not have access to f’s variables. In a dynamic scope, the value of a variable is checked layer by layer from the inside out through the function call chain, and the value of the binding first encountered is printed. Obviously, the outermost binding is the value in the global state.

Languages that use dynamic scoping are Pascal, Emacs Lisp, Common Lisp (both static scoping), and Perl (both static scoping). C/C++ is a statically scoped language, but the names used in macros are also dynamically scoped.

reference

scope