Scope is one of the most important concepts in JavaScript. If you want to learn JavaScript well, you need to understand the working principles of JavaScript scope and scope chain. Today's article provides a brief introduction to JavaScript scope and scope chain, hoping to help everyone learn JavaScript better.
JavaScript Scope
Any programming language has the concept of scope. Simply put, scope is the accessible range of variables and functions. That is, scope controls the visibility and life cycle of variables and functions. In JavaScript, there are two types of variable scope: global scope and local scope.
1. Global Scope
Objects that can be accessed anywhere in the code have global scope. Generally speaking, there are several situations where global scope is available:
(1) The outermost function and variables defined outside the outermost function have global scope, for example:
(3) All window object properties have global scope
Generally, the built-in properties of the window object have global scope, such as window.name, window.location, window.top, etc.
2. Local Scope
Contrary to the global scope, the local scope is generally only accessible within a fixed code fragment, the most common one is within a function, so in some places you will see people refer to this scope as a function scope. For example, blogName and function innerSay in the following code only have local scope.
In JavaScript, functions are also objects. In fact, everything in JavaScript is an object. Function objects, like other objects, have properties that can be accessed through code and a set of internal properties that are only accessible to the JavaScript engine. One of the internal properties is [[Scope]], defined by the ECMA-262 standard third edition. This internal property contains the collection of objects in the scope in which the function is created. This collection is called the scope chain of the function, which determines Which data can be accessed by the function.
When a function is created, its scope chain is populated with data objects accessible in the scope in which the function was created. For example, define the following function:
The scope of function add will be used during execution. For example, execute the following code:
The values are copied into the runtime context's scope chain in the order they appear in the function. Together they form a new object called an "activation object". This object contains all the local variables, named parameters, parameter collections and this of the function. Then this object will be pushed to the front end of the scope chain. When the runtime context is destroyed, the active object is also destroyed. The new scope chain is as shown below:
During the execution of the function, if a variable is not encountered, it will go through an identifier parsing process to determine where to obtain and store the data. This process starts from the head of the scope chain, that is, from the active object, and looks for an identifier with the same name. If it is found, use the variable corresponding to this identifier. If it is not found, continue to search for the next object in the scope chain. If If no objects are found after searching, the identifier is considered undefined. During function execution, each identifier undergoes such a search process.
Scope chaining and code optimization
It can be seen from the structure of the scope chain that the deeper the identifier is located in the scope chain of the runtime context, the slower the reading and writing speed will be. As shown in the figure above, because global variables always exist at the end of the runtime context scope chain, finding global variables is the slowest during identifier resolution. Therefore, when writing code, you should use global variables as little as possible and use local variables as much as possible. A good rule of thumb is: if a cross-scope object is referenced more than once, store it in a local variable before using it. For example, the following code:
document.getElementById("btnChange").onclick=function(){
document.getElementById("targetCanvas").style.backgroundColor="red";
};
}
var doc=document;
doc.getElementById("btnChange").onclick=function(){
doc.getElementById("targetCanvas").style.backgroundColor="red";
};
}
Change scope chain
The corresponding runtime context is unique each time a function is executed, so calling the same function multiple times will result in the creation of multiple runtime contexts. When the function completes execution, the execution context will be destroyed. Each runtime context is associated with a scope chain. Normally, while the runtime context is running, its scope chain will only be affected by with statements and catch statements.
The with statement is a shortcut way to use objects to avoid writing repeated code. For example:
with(document){
var bd=body,
links=getElementsByTagName("a"),
i=0,
len=links.length;
while(i < len){
update(links[i ]);
}
getElementById("btnInit").onclick=function(){
doSomething();
};
}
}
When the code runs to the with statement, the scope chain of the runtime context is temporarily changed. A new mutable object is created that contains all the properties of the object specified by the parameter. This object will be pushed into the head of the scope chain, which means that all local variables of the function are now in the second scope chain object, and therefore more expensive to access. As shown below:
Therefore, you should avoid using the with statement in your program. In this example, simply storing the document in a local variable can improve performance.
Another thing that changes the scope chain is the catch statement in the try-catch statement. When an error occurs in the try code block, the execution process jumps to the catch statement, and then the exception object is pushed into a mutable object and placed at the head of the scope. Inside the catch block, all local variables of the function will be placed in the second scope chain object. Sample code:
doSomething();
}catch(ex){
alert(ex.message); //The scope chain changes here
}
doSomething();
}catch(ex){
handleError(ex); //Delegate to the processor method
}