Poor road, feeling, JS pit, is not generally large.
Variable promotion:
Variable promotion (as).
I hate the var keyword:
After reading the following, you’ll understand what the title means, starting with a super simple piece of code:
<script type="text/javascript">
var str = 'Hello JavaScript hoisting';
console.log(str); // Hello JavaScript hoisting
</script>
Copy the code
This code, surprisingly simple, delivers the desired result, printed in the console: Hello JavaScript.
Now, I’m going to change this code, put the call in front and the declaration in back.
Many languages such as C or C++ do not allow this, but javaScript does.
Try to guess the result:
<script type="text/javascript">
console.log(str); // undefined
var str = 'Hello JavaScript hoisting';
console.log(str); // Hello JavaScript hoisting
</script>
Copy the code
You’ll wonder why our STR = undefined before we call it instead of an error: undefined??
Var STR = ‘Hello JavaScript ‘as of July 1, 1997;
<script type="text/javascript">
console.log(str); // Uncaught ReferenceError: str is not defined
</script>
Copy the code
Now we have what we want, error: undefined.
In fact, our browser will first parse through our script, completing one of the initialization steps it encountersvar
Variable is initialized to beundefined
。
This is called variable promotion (as of July 1), which means that the browser encounters an initialization of the JS execution environment, causing the variables to be defined in advance.
In the above code, we didn’t touch on functions, because I wanted to make the code more concise and shallow, and obviously we should test functions.
<script type="text/javascript"> console.log(add); ƒ add(x, y) {return x + y; }
function add(x, y) {
return x + y;
}
</script>
Copy the code
In this case, we’re not calling the function, but the function is already initialized, and there’s more to it than meets the eye.
How to avoid variable promotion:
Use let and const, use const whenever possible, and avoid var whenever possible;
<script type="text/javascript"> // console.log(testvalue1); Testvalue1 is not defined //let testvalue1 = 'test'; / * -- -- -- -- -- -- -- -- -- I am your line -- -- -- -- -- -- -- * / console log (testvalue2); Testvalue1 is not defined const testValue2 ='test';
</script>
Copy the code
However, if it is for compatibility, there is no way, ha ha ha, a fatal blow!!
Execution context:
An execution context, also known as an execution context, sounds great, right? It’s not that hard.
Scope chain:
In fact, we know that JS uses lexical scope.
If you don’t know anything about other scopes, please go to my talk about JavaScript scopes or baidu.
Each javaScript Function is represented as an object, or more specifically, an instance of the Function object.
Function objects, like other objects, have programmatically accessible properties. And a set of properties that cannot be accessed by code, which are internal properties provided to the JavaScript engine for access. One of these attributes is [[Scope]], defined by the ECMA-262 standard, third edition.
The inner attribute [[Scope]] contains the collection of objects in the Scope in which the function was created.
This set is called the function’s scope chain, and it determines which data can be accessed.
Source: High Performance JavaScript;
I wonder, how can I see this property, which is not accessible through code? After my husband’s research, the way to see this thing;
Open Console for Chrome and type in the following code:
function add(x, y) {
returnx + y; } console.log( add.prototype ); // As can be seen from the constructor on the prototype chain, the add function has hidden attributes.Copy the code
There could be other ways, but. this is the only one I’ve found.
Here’s what you need:
And then this:
Ok, so you’ve seen that under the [[Scope]] property is an array that holds the chain of scopes, and there’s only one global.
Think about the following code and review the lexical Scope. Think about it with the [[Scope]] property to understand how lexical Scope works.
var testValue = 'outer';
function foo() {
console.log(testValue); // "outer"Console. log(foo.prototype) // number 1}function bar() {
var testValue = 'inner'; Console. log(bar.prototype) // number 2 foo(); } bar();Copy the code
The following is the execution result:
The [[Scope]] property at # 1: Scopes[1] :
The [[Scope]] property at no. 2: Scopes[1]
Since [[Scope]] is already defined at initialization, both functions will look for variables in the global Scope if their own Scope is not found.
Consider another piece of code:
var testValue = 'outer';
function bar() {
var testValue = 'inner'; foo(); Console. log(bar.prototype) // Number 1function foo() {
console.log(testValue); // "inner"console.log(foo.prototype); }} bar();Copy the code
The [[Scope]] property at # 1: Scopes[1] :
Scopes[2] : Scopes[2] :
So that explains why testValue is equal to “inner”.
When the testValue variable needs to be called;
First find itself scope, no, JS engine will find down the scope chain [0] = > [1] = > [2] = > […]. .
Here, you find the bar function scope, and interestingly enough, Closure means Closure.
Prove that the global scope chain is determined when the global execution context is initialized:
As an interesting experiment, you can find the [[Scope]] property in the way I described earlier.
When was this attribute determined??
Obviously, we need to test before the function is declared, while the function is executing, and after the function is executed:
console.log(add.prototype); // Number 1 before declarationfunctionadd(x, y) { console.log(add.prototype); // Number 2 runtimereturnx + y; } add(1, 2); console.log(add.prototype); // Number 3 After executionCopy the code
No. 1 Before:
No. 2 Runtime:
No. 3 After execution:
You can experiment as many times as I did, trying to nest several functions and observing the chain of scopes before calling them.
The scope chain, which is the context in which the JS engine completes initialization execution, has been determined, as discussed in the variable promotion section.
It ensures that the JS internal normal query we need variables! .
A little bit of confusion
Note: I can’t prove a problem here.
- After the global execution context is initialized, it defines all function scope chains.
- Again, initialize an execution context, specifying the scope chain of the function in this scope.
This is my doubt, I can not prove this problem, but I prefer the view of 2, if you know how to prove, please contact me. At least, that’s how it’s described in High Performance JavaScript.
What are the benefits of knowing the scope chain?
Imagine, we know the scope chain, what use??
We know that if the scope chain is deeper, [0] => [1] => [2] => […] => [n], we call the global variable, it is always the last (here is the NTH), how much of a performance problem is this finding the variable we need? How much time does the JS engine take to find variables?
So, the lesson of this story is to try to localize global variables to avoid the performance problems associated with layer upon layer nesting of the scope chain.
Understand the execution context:
Put this code under the global scope. This code is adapted from High Performance JavaScript.
function add(x, y) {
return x + y;
}
var result = add(1, 2);
Copy the code
This code is also neat, but what happens inside the JavaScript engine is not.
As discussed in the previous section on variable promotion, the JS engine initializes our declared functions and variables.
Add (1, 2) : [Scope] : [Scope] : [Scope] : [Scope] : [Scope] : [Scope] : [Scope] : [Scope] : [Scope] : [Scope] : [Scope] : [Scope] : [Scope] : [Scope] : [Scope]
There are three phases: initialize the execution context, run the execution context, and close the execution context.
Var result = add(1, 2); var result = add(1, 2);
As shown in the figure above, the chain of scopes held by the [[Scope]] property of the add function already contains these things before the function is called.
When this function is executed, an internal object called the execution Context is created.
An execution context defines the environment in which a function is executed. Each time a function is called, an execution context is created.
Once the execution context is successfully initialized, an active object is created that generates this arguments and the variables we declare, in this case x and y.
Run the execution context phase:
End execution context phase:
Ok, however, there is no mention of calling other functions here.
In fact, how does our JavaScript engine manage the execution context between functions?
Manage multiple execution contexts, actually use the context execution stack for details please refer to the link:
Reference and Acknowledgements:
- This article is a reference to High Performance JavaScript