This is the ninth day of my participation in the August More Text Challenge. For more details, see the August More Text Challenge

preface

Scope is a language-independent concept that you should be familiar with if you’ve worked with languages like Lisp or Scheme. This article will take you through what lexical scope and dynamic scope are, and discuss the lexical scope of JavaScript, so let’s get started.

What is lexical scope and dynamic scope?

Scope, I get it. What about the unfamiliar prefix “lexical” and “dynamic”?

Don’t worry, let’s explain slowly:

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.

In JavaScript, when you say “lexical scope,” you’re talking about the scope of JS.

At the language level, 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 it’s the one we’ll focus on
  • Dynamic scope: Less popular, but some languages do adopt dynamic scope, such as Bash scripts, Perl, and so on

In fact, as a programmer, you should not be limited to only one programming language, because you know several programming languages, can help you learn the language differences, you can learn more knowledge.

Learn a new language and see how different languages design and solve problems.

Lexical scope

As an example, let’s look at:

let name = Dream Chaser;

function showName() {
    console.log(name);
}

function changeName() {
    let name = 'experts';
    showName();
}

changeName();

// The output is?
Copy the code

The output is “Dream Chaser”. This is because JS takes a lexical (static) scope, so let’s examine the process of running this code to locate variables:

  • The showName function uses the variable name in its scope, so it first looks inside itself to see if there is a local variable name
  • The value of name is “Dream Player”, so it prints out “Dream Player”. If the value of name is “Dream Player”, it prints out “dream Player”.

The scope relationships are shown as follows:

The scope chain at runtime is as follows:

In this case, the scope is defined during the writing process (in the example above, when the function is defined, the block-level scope is also defined during the code block), depending on where you put it.

Scopes like this follow the lexical scope model.

In short, JavaScript uses lexical scope, and the scope of a function is determined when the function is defined.

Dynamic scope

let name = Dream Chaser;

function showName() {
    console.log(name);
}

function changeName() {
    let name = 'experts';
    showName();
}

changeName();

// The output is?
Copy the code

What does the above code look like under the dynamic scope mechanism?

Assuming that JavaScipt adopts dynamic scope, we analyze the execution process:

  • The showName function uses the variable name in its scope, so it first looks inside itself to see if there is a local variable name
  • No, so we continue looking for name where showName was called along the function call stack. Since showName is called from changeName, changeName has a variable named, so that name is referred to showName.

In this case, the scope chain is shown as follows:

So if the scope is dynamic, then the above code will run as a “brick”.

So how do I verify that in dynamic scope, the result will be “brick expert”?

We can rewrite the js code above using bash language. Because bash is dynamic scoping.

name=Dream Chaser;
function showName() {
    echo $name;
}
function changeName() {
    local name='experts';
    showName;
}
changeName;
Copy the code

Store the above script to scope.bash, then go to the corresponding directory, run the bash./scope.bash command, and see what the printed value is. You can try the results, see the figure:

Let’s conclude that the difference between lexical scope and dynamic scope is actually the timing of the scope:

  • Lexical scope: When the code is divided as it is written, the scope chain extends outward from where it is defined
  • Dynamic scope: When code is divided at run time, the scope chain extends out along its call stack

Another way to say it:

  • Lexical scope: The scope of a function is determined when the function is defined.
  • Dynamic scope. The scope of a function is determined when the function is called.

Of course, that one is better understood, so let’s just remember that one.

Cheat/modify lexical scope

I don’t know if you’ve heard of it? How to “trick” lexical scope? Questions like this.

In fact, “cheat” means change. JS is already scoped at the code writing stage. So what are we going to do, at runtime, to change the scope that you have. We’ve included Eval and With, just to broaden your knowledge and interest, so you can read on:

Tips: Don’t write code with and eval

In fact, with and eval have long been a bit of a throwback to us JS programmers because of their annoying side effects, such as the performance drag on the language, our “outborn” global variables, etc.

eval

The eval command executes a string as a statement.

eval('var a = 1; ');
console.log(a); / / 1
Copy the code

The above code runs the string as a JS statement, generates the variable A, and prints 1. So this is the same thing as

var a = 1;
console.log(a); / / 1
Copy the code

Let’s look at this example again:

function showName(str) {
  eval(str);
  // Execute the eval statement, which is equivalent to executing the following code
  // var name = "";
  console.log(name);
}

var name = Dream Chaser;
var str = 'var name = ' ';

showName(str); // "Brick expert"
Copy the code

When we print name, we do not go to the upper scope (global scope) to find the name variable, because the eval statement is executed and the name variable already exists in the scope.

Because eval(STR); In other words, you have successfully “modified” the scope that was defined at the writing stage under lexical scope rules.

Note the changes before and after eval: Important: The white area after eval

with

The with keyword can treat an object with no or more attributes as a fully isolated lexical scope, and the object’s attributes are treated as lexical identifiers defined in that lexical scope.

A typical use of with is as follows:

var obj = { name: Dream Chaser };
console.log(obj.name); // "Dream Chaser"
with(obj){
    name = 'experts';
}
console.log(obj.name); // "Brick expert"
Copy the code

When I modify the code like this, what’s the output?

var obj = { name: Dream Chaser };
console.log(obj.name); // "Dream Chaser"
with(obj){
    age = 18;
}
console.log(obj.age); // undefined
console.log(age); / / 18
Copy the code

Why is it that if I print age, I get 18? Because when a property that is not in the object is assigned in the scope created by with, no property is added to the object. However, the assigned identifier is leaked to the global scope, so the result of printing age is 18.

For more information on with, see the reference article.

reference

  • Explore the lexical scope model
  • JavaScript in-depth lexical scope and dynamic scope
  • Closures – Explore lexical scoping models
  • JavaScript in-depth lexical scope and dynamic scope

Feel free to correct any mistakes in the comments section, and if this post helped or liked you, feel free to like or follow.