In JavaScript, the scope chain of a function is a difficult thing to understand. This is because the scope chain of functions in JavaScript is far from the scope chain of functions in other languages such as C and C + +. This article explains in detail the knowledge about the scope chain of functions in JavaScript, and understanding this knowledge can help you avoid some of the problems that may arise when dealing with closures.
In JavaScript, a function allows you to perform a series of operations in a single call. There are several ways to define a function, as follows:
1. Function declaration:
function Maximum (x, y) { ifreturn x; Else return y;} Maximum (56// return 6;
This syntax is commonly used to define functions under global scope (global functions)
2. Function expression:
var New = function (x, y) { ifreturn x; Else return Y;};o Bj.maximum (56// return 6;
This syntax is often used to define a function as an object method
3. Function constructor:
var New Function ("x""y""If" (X > Y) return x; else return y; " ); Maximum (56// return 6;
Defining functions in this form is usually not very readable (no indentation) and is used only in certain situations.
function Definition:
A function definition refers to the process of creating a function object inside the JavaScript engine.
If it is a global function, the function object will be added to the global object as an attribute;
In the case of an intrinsic function (nested function), the function object is added as an attribute to the active object of the upper function, and the property name is the function name. It should be noted that if a function is defined as a function declaration, the function's definition operation occurs at the time of script parsing.
In the following example, when the JavaScript engine completes the script parsing, a function object func has been created, and the function object is added as a property to the global object with the property name "Func".
/* func function can be accessed to , because the Func function already exists before the script starts executing. */ alert (func ( 2 )); // returns 8 // Executing the statement overrides the value of Func to True. var func = true ;alert (func); // /* */ function func (x) { return x * x * x;}
In the following example, there is the case of an intrinsic function. The definition of an intrinsic function INNERFN occurs when an external function OUTERFN executes (which in fact occurs in the parsing phase before execution), and the intrinsic function is added as an attribute on the active object of the external function.
function Outerfn () { //// OUTERFN function is defined when executing INNERFN
Note: For functions defined with the function constructor, function definition operations occur when executing a function constructor.
Scope Chain:
The scope chain of a function is made up of a series of objects (the active object of the function + 0 to multiple upper-level functions + the last global object).
When the function is executed, the values of the identifiers used in the function body are searched in order of their properties (identifier resolution), and the function stores the scope chains of their respective environment (global context or function context) in their own [[scope]] internal properties when defined.
First look at an example of an intrinsic function:
function Outerfn (i) { return function Innerfn () { return i; }} var innerfn = OUTERFN (4// return 4
When the INNERFN function executes, the value 4 of the variable i is returned successfully, but the variable i does not exist in the local variable of the INNERFN function itself or in the global scope, then where does the value of the variable I get from? You might think that the scope chain of the intrinsic function INNERFN is made up of the active object + global object of the INNERFN function, but this is not true, only the scope chain of the global function contains two objects, which does not apply to intrinsic functions. Let's first analyze the global functions and then analyze the intrinsic functions.
Global functions:
The scope chain of global functions is well understood.
var Ten ; var 0 ; function Testfn (i) { vartrue; 1 ; alert (i);} TESTFN (ten);
Global object: The JavaScript engine creates a global object before the script starts executing and adds it to some predefined properties such as "Infinity", "Math", and so on. Global variables defined in the script also become properties of the global object.
Active objects: When the JavaScript engine calls some functions, the function creates a new active object, all local variables defined inside the function, and the named arguments and arguments objects that pass into the function as properties of the active object. This activity object, together with the scope chain stored in the function's [[scope]] intrinsic properties, makes up the scope chain of this function call.
intrinsic functions:
Let's analyze the JavaScript code below.
function outerfn (i, j) { var x = i + J; return function innerfn (x) { return i + x; }} var func1 = OUTERFN (5 , 6 var func2 = OUTERFN (10 , 20 10 )); // alert (FUNC2 (10 )); // return
When you call Func1 (10) and Func2 (10), you refer to two different I. What's going on? First look at the following statement:
var func1 = OUTERFN (5,6);
When calling OUTERFN (5, 6), a new function object INNERFN is defined, and then the function object becomes a property of the active object of the OUTERFN function. The scope chain of the INNERFN is then composed of the OUTERFN's active object and the global object, which is stored in the internal property [[scope]] of the INNERFN function, and the function is returned, and the variable func1 points to the INNERFN function.
Alert (FUNC1 (10));//Return 15
When FUNC1 is called, its own active object is created and then added to the front of the scope chain stored in [[scope]] (the new scope chain does not change the scope chain stored in [[scope]]. The scope chain at this point is the scope chain that is used when the FUNC1 function executes. From this scope chain, you can see that the value of the variable ' I ' is actually the value of the property I of the active object that was produced when the OUTERFN (5,6) was executed.
Now let's go back to the question, "when calling Func1 (10) and Func2 (10), you quoted two different i. What's going on? "。
The answer is that when you define FUNC1 and Func2, two different active objects are generated in the function OUTERFN.
Now there is another problem, an activity object is created when the function is executed, but it will not be destroyed when the function is finished. I use the following three examples to explain this problem.
(1) functions with no intrinsic functions
function Outerfn (x) { return x * x;} var y = Outerfn (2);
If the function does not have an intrinsic function, the currently active object is added to the front of the scope chain of the function when the function executes.
A scope chain is the only place where this activity object is referenced. When the function exits, the active object is removed from the scope chain and is subsequently destroyed by the garbage collector because there is no reference to the active object anywhere.
(2) A function that contains an intrinsic function, but the intrinsic function is not referenced by a variable outside the external function
function outerfn (x) { // No reference to square outside OUTERFN function Square (x) { return x * x; // There is no reference to cube outside of OUTERFN function cube (x) { return x * x * x; var temp = square (x); return temp/2 var y = outerfn (5 );
In this case, the active object that is created when the function is executed is added not only to the front end of the scope chain of the current function, but also to the scope chain of the inner function.
When the function exits, the active object is removed from the scope chain of the current function, the active object and the inner function refer to each other, and the active object of the OUTERFN function refers to the nested function object square and cube. An intrinsic function object that references the active object of the OUTERFN function in the scope chain of square and cube. However, because they have no external references, they will be reclaimed by the garbage collector.
(3) A function that contains an intrinsic function, except that there is a reference to the intrinsic function outside the outer function (closure)
2 Types of cases:
function outerfn (x) { // intrinsic function as the return value of OUTERFN is referenced to external return function Innerfn () { return x * x; }} // Referencing the returned intrinsic function var square = OUTERFN (5
var square;function OUTERFN (x) { // is referenced by a global variable to the inner function square = function Innerfn () { return x * x;} } OUTERFN (5); Square ();
In this case, the active object created when the OUTERFN function is executed is added not only to the front end of the scope chain of the current function, but also to the scope chain of the inner function innerfn (INNERFN [[scope]] internal properties).
When an external function OUTERFN exits, its active object is removed from the current scope chain, but the scope chain of the inner function innerfn still references it. Since an external reference square exists for the intrinsic function INNERFN, and the scope chain of the inner function INNERFN still references the active object OUTERFN the external function, the value of the variable x stored on the OUTERFN's active object can still be accessed when INNERFN is called.
multiple intrinsic functions:
The more interesting scenario is that there is more than one intrinsic function, and the scope chain of multiple intrinsic functions refers to the active object of the same external function. The change of the active object will be reflected on three internal functions.
function Createcounter (i) {function increment () {++i; } function Decrement () {--i; } function GetValue () {returni; } function Counter (increment, decrement, getValue) { This. Increment =increment; This. Decrement =decrement; This. GetValue =GetValue; } return NewCounter (increment, decrement, getValue);}varCounter = Createcounter (5); counter.increment (); alert (Counter.getvalue ()); //returns 6
The above example shows that the active object of the Createcounter function is shared by the scope chain of three internal functions.
Closures and circular references:
The scope chain of functions in JavaScript is discussed above, and the problem of memory leaks in closures may occur due to circular references. Closures are usually referred to as intrinsic functions that can be called outside the external function. An example is given below:
function Outerfn (x) { = function Innerfn () {}}var div = document.createelement (" Div"); Outerfn (div);
In the example above, there is a circular reference between a DOM object and a JavaScript object. The DOM Object Div refers to the intrinsic function INNERFN through the property ' func '. The property ' x ' of the active object on the inner function INNERFN's scope chain (stored on the internal property [[scope]]) references the DOM object Div. Such a circular reference can cause a memory leak.
A summary of the function scope chain in JS