Original article: http://blogs.msdn.com/ B /jscript/archive/2007/07/26/scope-chain-of-jscript-functions.aspx
In JavaScript, the function scope chain is hard to understand. this is because the scope chain of functions in JavaScript is far from that in other languages such as C and C ++. this article explains in detail the knowledge related to function scope chains in JavaScript. Understanding this knowledge can help you avoid some possible problems when processing closures.
In JavaScript, functions allow you to execute a series of operations in a single call. There are multiple methods to define a function, as shown below:
Function declaration:
Function maximum (x, y) {if (x> y) return x; else return y;} maximum (5, 6) // return 6;
This syntax is usually used to define functions (global functions) under the global scope ).
Function expression:
Var obj = new Object (); obj. maximum = function (x, y) {if (x> y) return x; else return y ;};
Obj. maximum (5, 6) // return 6;
This syntax is usually used to define a function as an object method.
Function Constructor:
Var maximum = new Function ("x", "y", "if (x> y) return x; else return y;"); maximum (5, 6 ); // return 6;
Defining a function in this form usually does not have good readability (no indentation) and can be used only in specific situations.
Function Definition:
Function definition refers to the process of creating a function object within the JavaScript engine. if it is a global function, this function object will be added to the global object as an attribute. If it is an internal function (nested function, this function object will be added as an attribute to the activity object of the Upper-layer function. The attribute name is the function name. it should be noted that if a function is defined in the form of a function declaration, the function definition operation will take place during script parsing. in the following example, when the JavaScript engine completes script parsing, a function object func has been created. This function object is added to the global object as an attribute and the property name is "func ".
/* The func function can be accessed, because the func function already exists before the script starts execution. */alert (func (2); // return 8
// Executing this statement will overwrite the value of func to true. var func = true;
Alert (func); // returns "true ";
/* Before the script starts execution, parse the following statement to define a function object func. */function func (x) {return x * x ;}
In the following example, internal functions exist. the operation defined by the internal function innerFn occurs when the external function outerFn is executed (in fact, it also occurs in the parsing phase before execution). At the same time, the internal function is added as an attribute to the activity object of the external function.
Function outerFn () {function innerFn () {}} outerFn (); // an innerFn function is defined when the outerFn function is executed.
Note: For a Function that uses the Function to construct a Function definition, the Function definition operation occurs when the Function constructor is executed.
Scope chain:
The function scope chain is composed of a series of objects (function activity objects + 0 to multiple upper-layer function activity objects + final global objects). During function execution, the values of the identifiers used in the function body are searched from the attributes of these objects in sequence (identifier resolution ). when defining a function, the function stores the scope chain of its own environment (Global context or function context) to its internal [scope] attributes. first, let's look at an example of an internal function:
Function outerFn (I) {return function innerFn () {return I ;}} var innerFn = outerFn (4); innerFn (); // return 4
When the innerFn function is executed, the value 4 of 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. where can I get the value of variable I? You may think that the scope chain of the innerFn function is composed of the innerFn function's active object + global object. however, this is incorrect. Only the global function's scope chain contains two objects, which is not applicable to internal functions. let's analyze global functions first, and then analyze internal functions.
Global Functions:
The scope chain of global functions is easy to understand.
var x = 10;var y = 0;function testFn(i) { var x = true; y = y + 1; alert(i);}testFn(10);
Global Object: the JavaScript engine creates a global object before the script starts execution and adds it to some predefined attributes such as "Infinity" and "Math. the global variables defined in the script will also become attributes of the global object.
Activity object:When the JavaScript engine calls some functions, the function creates a new activity object, all the local variables defined within the function, as well as the name parameters and arguments objects of the input function will be attributes of this activity object. this activity object and the scope chain stored in the [[scope] of the function constitute the scope chain of this function call.
Internal functions:
Let's analyze the JavaScript code in the pipeline.
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); alert (func1 (10); // returns 15 alert (func2 (10); // returns 20
When you call func1 (10) and func2 (10), you reference two different I. What is the problem? First, let's look at the following statement,
var func1 = outerFn(5,6);
When outerFn (5, 6) is called, a new function object innerFn is defined, and the function object becomes an attribute of the activity object of the outerFn function. at this time, the scope chain of innerFn is composed of outerFn activity objects and global objects. this scope chain is stored in the innerFn function's internal attribute [[scope], and then this function is returned. The variable func1 points to this innerFn function.
Alert (func1 (10); // returns 15
When func1 is called, its own activity object is created and then added to the front of the scope chain stored in [[scope] (new scope chain, does not change the scope chain stored in [[scope ). at this time, the scope chain is used for func1 function execution. from this scope chain, you can see that the value of the variable 'I' is actually the property I value of the activity object generated when outerFn (5, 6) is executed. the entire process is displayed.
Now let's go back to the question: "When you call func1 (10) and func2 (10), you reference two different I. What is the problem? ". Let's take a look at the execution of func2. The answer is that two different activity objects are generated in the outerFn function when func1 and func2 are defined.
Now there is another problem: an active object is created during function execution, but will not be destroyed after function execution is complete? I will use the following three examples to explain this problem.
I) functions without internal functions
function outerFn(x) { return x * x;}var y = outerFn(2);
If a function does not have an internal function, the current active object will be added to the frontend of the function's scope chain during function execution. the scope chain is the only place that references this activity object. when the function exits, the activity object will be deleted from the scope chain. Since there is no reference to this activity object, it will be destroyed by the garbage collector.
Ii) functions that contain internal functions. However, this internal function is not referenced by variables other than external functions.
Function outerFn (x) {// no reference to function square (x) {return x * x;} pointing to square outside outerFn ;} // no reference to cube outside outerFn function cube (x) {return x * x;} var temp = square (x); return temp/2 ;} var y = outerFn (5 );
In this case, the activity object created during function execution is not only added to the front end of the current function's scope chain, but also to the internal function's scope chain. when the function exits, the activity object will be deleted from the scope chain of the current function. The activity object and the internal function reference each other, the activity object of the outerFn function references the nested function objects square and cube. The activity object of the outerFn function is referenced in the scope chain of the internal function objects square and cube. however, since none of them have external references, they will all be recycled by the garbage collector.
Iii) a function that contains an internal function, but a reference pointing to this internal function exists outside the external function.
Example 1:
Function outerFn (x) {// The internal function as the return value of outerFn is referenced to the external return function innerFn () {return x * x ;}} // reference the returned internal function var square = outerFn (5); square ();
Example 2:
Var square; function outerFn (x) {// reference the internal function square = function innerFn () {return x * x ;}} outerFn (5) through a global variable ); square ();
In this case, the activity object created during the execution of the outerFn function is not only added to the front end of the scope chain of the current function, it is also added to the scope chain of the innerFn internal function (innerFn's [[scope] internal attributes ). when the external function outerFn exits, although its activity object is deleted from the current scope chain, the innerFn scope chain still references it. because the internal function innerFn has an external reference square and the scope chain of the innerFn function still references the activity object of the external function outerFn, when innerFn is called, you can still access the value of variable x stored on the outerFn activity object.
Multiple internal functions:
A more interesting scenario is that there are more than one internal function. The scope chains of multiple internal functions reference the activity objects of the same external function. changes to the activity object are reflected in three internal functions.
Function createCounter (I) {function increment () {++ I;} function decrement () {-- I;} function getValue () {return I;} function Counter (increment, decrement, getValue) {this. increment = increment; this. decrement = decrement; this. getValue = getValue;} return new Counter (increment, decrement, getValue);} var counter = createCounter (5); counter. increment (); alert (counter. getValue (); // returns 6
Indicates that the activity object of the createCounter function is shared by the scope chains of the three internal functions.
Closures and circular references:
The scope chain of functions in JavaScript is discussed above. The memory leakage may occur in the closure during the next interview. A closure usually refers to an internal function that can be called outside an external function. the following is an example:
function outerFn(x) { x.func = function innerFn() {}}var div = document.createElement("DIV");outerFn(div);
In the preceding example, a circular reference exists between a DOM object and a JavaScript Object. the DOM object div references the innerFn internal function through the attribute 'func. the property 'X' of the activity object on the innerFn scope chain (stored on the internal attribute [scope]) references the DOM object div. such loop reference may cause memory leakage.
Note: I guess the author intentionally did not mention the concept of execution context to make the article easier to understand. I added the Internal Attributes of [[scope] in this article.