Home >Web Front-end >JS Tutorial >JavaScript object model-execution model_js object-oriented

JavaScript object model-execution model_js object-oriented

WBOY
WBOYOriginal
2016-05-16 19:04:511197browse

Simple numerical types: There are Undefined, Null, Boolean, Number and String. Note that the English words in the description here only refer to the name of the data type, and do not specifically refer to JS’s global objects Nan, Boolean, Number, String, etc. Their conceptual differences are relatively large.
Object: An unordered collection of attributes whose values ​​are simple numeric types, objects or functions. Same as above, the object here does not specifically refer to the global object Object.
Function: Function is a type of object. In implementation, the value of the internal attribute [[Class]] is "Function", indicating that it is a function type. In addition to the internal attribute methods of the object, there are also [[Construct]], [ [Call]], [[Scope]] and other internal properties. The processing mechanism of functions as function calls is different from that of constructors (using the new keyword to create instance objects) (except for Function objects). The internal method [[Construct]] is used to implement the logic of the constructor, and the method [[Call]] implements Logic as a function call. Same as above, the function here does not specifically refer to the global object Function.
Function can be regarded as a class of object-oriented language in the prototype language of JS, which can be used to construct object instances. Since functions can be viewed as classes, each function can be viewed as an extended data type.

Built-in data type (built-in object)
Function: User interface of function type.
Object: User interface of object type.
Boolean, Number, String: These are object wrappers for these three simple numerical types. Object wrapping is conceptually similar to Box/Unbox in C#.
Date, Array, RegExp: They can be regarded as several built-in extended data types.

First of all, Function, Object, Boolean, Number, String, Date, Array, RegExp, etc. are all built-in objects of the JavaScript language. They can all be regarded as derived types of functions. For example, Number instanceof Function is true. Number instanceof Object is true. In this sense, they can be treated identically to user-defined functions.
Secondly, each of them can represent a data type, which is implemented by the JS engine using native code or built-in JS code. It is an interface exposed to developers to operate these built-in data types. In this sense, they are all abstract concepts, with specific implementation mechanisms hidden behind them.
Every time you mention words such as Number, Function, etc., you should quickly instantiate them in your mind into one of the two situations above.

Data type implementation model description

Build-in *** data structure: refers to the data structure used internally in JS to implement *** types. These The structure is basically inaccessible to us directly.
Build-in *** object: refers to JS’s built-in Number, String, Boolean and other objects. This is the interface through which JS exposes internally implemented data types to developers.
Build-in *** constructor: refers to some constructors built into JS, used to construct object instances of corresponding types. They are packaged and exposed as function objects. For example, we can access these function objects using the following method:

//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
//
access the build-in number constructor
var number = new Number(123);
var numConstructor1 = number.constructor; //or
var numConstructor2 = new Object(123).constructor;
//both numConstructor1 and numConstructor2 are the build-in Number constructor
numConstructor1 == numConstructor2 //result: true
//
access the build-in object constructor
var objConstructor1 = {}.constructor; //or
var objConstructor2 = new Object().constructor;
//both objConstructor1 and objConstructor2 are the build-in Object constructor
objConstructor1==objConstructor2 //result: true

具体实现上,上图中横向之间可能也存在关联,例如对于build-in data structure和constructor,Function、 Date、 Array、 RegExp等都可以继承Object的结构而实现,但这是具体实现相关的事情了。

关于简单数值类型的对象化
这是一个细微的地方,下面描述对于Boolean, String和Number这三种简单数值类型都适用,以Number为例说明。
JS规范要求: 使用var num1=123;这样的代码,直接返回基本数据类型,就是说返回的对象不是派生自Number和Object类型,用num1 instanceof Object测试为false;使用new关键字创建则返回Number类型,例如var num2=new Number(123); num2 instanceof Number为true。
将Number当作函数调用,返回结果会转换成简单数值类型。下面是测试代码:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
var num1 = new Number(123); //num1 derived from Number & Object
num1 instanceof Number //result: true
num1 instanceof Object //result: true
//convert the num1 from Number type to primitive type, so it's no longer an instance of Number or Object
num1 = Number(num1);
num1 instanceof Number //result: false
num1 instanceof Object //result: false
var num2 = 123//num2 is a primitive type
num2 instanceof Number //result: false
num2 instanceof Object //result: false
Although we got a simple numerical type, it still looks like a JS Object object, with Object and all the properties and methods of the corresponding type. There is basically no difference in use. The only difference is the test result of instanceof.

Prototype inheritance
Prototype

Every object has an internal property of [[Prototype]], its The value is null or another object. Function objects have an explicit prototype attribute, which is not an internal [[Prototype]] attribute. Different JS engine implementers can name the internal [[Prototype]] attribute to any name and set its visibility for use only within the JS engine. Although the internal [[Prototype]] cannot be accessed in JS code (it can be accessed in FireFox, the name is __proto__ because Mozilla makes it public), you can use the object's isPrototypeOf() method for testing. Note that this method will be used throughout the Judgment is performed on the Prototype chain.
When using obj.propName to access the properties of an object, follow the steps below (assuming that obj’s internal [[Prototype]] property is named __proto__):
1. If obj has a propName property, return the property value, otherwise
2. If obj.__proto__ is null, return undefined, otherwise
3. Return obj.__proto__.propName
The method of calling the object is the same as accessing the attribute search process, because the function of the method An object is an attribute value of an object.
Tips: The above steps imply a recursive process. In step 3, obj.__proto__ is another object. Steps 1, 2, and 3 will also be used to search for the propName attribute.

For example, as shown in the figure below, object1 will have attributes prop1, prop2, prop3 and methods fn1, fn2, fn3. The dotted arrow in the figure represents the prototype chain.

This is inheritance and sharing based on Prototype. The method fn2 of object1 comes from object2. Conceptually, object2 overrides the method fn2 of object3.
JavaScript objects should all be related through the prototype chain. The top level is Object, that is, objects are all derived from the Object type.

Object-oriented languages ​​such as C use classes (abstracted types) to carry methods and objects (instantiated objects) to carry attributes. Prototype languages ​​only use instantiated objects to carry methods and attributes. The essential difference is that the former implements inheritance based on the description of the memory structure, while the latter implements it based on specific memory blocks.

Object creation process
Only function objects in JS have the concept of classes, so to create an object, you must use a function object. There are [[Construct]] method and [[Call]] method inside the function object. [[Construct]] is used to construct the object, [[Call]] is used for function calling. [[Construct] is only triggered when using the new operator. ]logic.
var obj=new Object(); uses the built-in Object function object to create the instantiated object obj. var obj={}; and var obj=[]; This kind of code will trigger the construction process of Object and Array by the JS engine. function fn(){}; var myObj=new fn(); creates an instantiated object using a user-defined type.

The creation process of new Fn(args) is as follows (that is, the [[Construct]] method of the function object handles the logic and the object creation process). In addition, the creation process of the function object itself (referring to defining a function or using Function to create a function object, etc.) also uses the following processing logic, but there are special places, which will be described later.
1. Create a build-in object object obj and initialize it
2. If Fn.prototype is Object type, set obj’s internal [[Prototype]] to Fn.prototype, otherwise obj’s [[Prototype ]] will initialize its value (i.e. Object.prototype)
3. Use obj as this and use the args parameter to call the internal [[Call]] method of Fn
3.1 The internal [[Call]] method creates the current execution Context
3.2 Call the function body of F
3.3 Destroy the current execution context
3.4 Return the return value of the function body of F. If the function body of F has no return value, return undefined
4. If [[ If the return value of Call]] is Object type, this value is returned, otherwise obj
Note that in step 2, prototype refers to the prototype property displayed by the object, and [[Prototype]] represents the object's internal Prototype property (implicit ).
What constitutes the object's prototype chain is the internal implicit [[Prototype]], not the object's explicit prototype attribute. The displayed prototype is only meaningful on the function object. As you can see from the above creation process, the prototype of the function is assigned to the implicit [[Prototype]] attribute of the derived object. In this way, according to the Prototype rules, the prototype objects of the derived object and the function Only the inheritance/sharing relationship of properties and methods exists between them.

Use code to do some verification:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
function fn(){}
//the value of implicit [[Prototype]] property of those objects derived from fn will be assigned to fn.prototype
fn.prototype={ attr1:"aaa", attr2:"bbb"};
var obj=new fn();
document.write(obj.attr1 
+ "
"); //result: aaa
document.write(obj.attr2 + "
"); //result: bbb
document.write(obj instanceof fn); //result: true
document.write("
");
//I change the prototype of fn here, so by the algorithm of Prototype the obj is no longer the instance of fn,
//
but this won't affect the obj and its [[Prototype]] property, and the obj still has attr1 and attr2 properties
fn.prototype={};
document.write(obj.attr1 
+ "
"); //result: aaa
document.write(obj.attr2 + "
"); //result: bbb
document.write(obj instanceof fn); //result: false
关于创建过程返回值的验证:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
function fn(){
   
//according to step 4 described above,
    //the new fn() operation will return the object { attr1: 111, attr2: 222 }, it's not an instance of fn!
    return { attr1: 111, attr2: 222 };
}
fn.prototype
={ attr1:"aaa", attr2:"bbb"};
var obj=new fn();
document.write(obj.attr1 
+ "
"); //result: 111
document.write(obj.attr2 + "
"); //result: 222
document.write(obj instanceof fn); //result: false

做个练习
经过上面的理解应,请写出下面这幅图的实现代码。图中CF是一个函数,Cfp是CF的prototype对象,cf1, cf2, cf3, cf4, cf5都是CF的实例对象。虚线箭头表示隐式Prototype关系,实线箭头表示显示prototype关系。
   
供参考的实现方案:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
function CF(q1, q2){
    
this.q1=q1;
    
this.q2=q2;
}
CF.P1
="P1 in CF"; 
CF.P2
="P2 in CF";
function Cfp(){
    
this.CFP1="CFP1 in Cfp";
}
CF.prototype
=new Cfp();
var cf1=new CF("aaa""bbb");
document.write(cf1.CFP1 
+ "
"); //result: CFP1 in Cfp
document.write(cf1.q1 + "
"); //result: aaa
document.write(cf1.q2 + "
"); //result: bbb

Local properties and inherited properties
Objects can inherit properties and methods through the implicit prototype chain, but prototype is also an ordinary object, that is to say, it is an ordinary instantiated object. rather than a purely abstract data structure description. So there is this problem of local properties and inherited properties.
First, let’s take a look at the process of setting object properties. JS defines a set of attributes that are used to describe the properties of objects to indicate whether the properties can be set in JavaScript code, enumerated by for in, etc. The steps for processing the assignment statement of
obj.propName=value are as follows:
1. If the attribute of propName is set to cannot be set, return
2. If obj.propName does not exist, create an attribute for obj , the name is propName
3. Set the value of obj.propName to value
. You can see that the value setting process does not consider the Prototype chain. The reason is obvious. The internal [[Prototype]] of obj is an instance. It is an object that not only shares properties with obj, but may also share properties with other objects. Modifying it may affect other objects.
Using the above examples of CF and Cfp to illustrate, the instance object cf1 has local attributes q1, q2 and inherited attribute CFP1. If cf1.CFP1="" is executed, then cf1 has the local attribute CFP1. The test results are as follows:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
var cf1=new CF("aaa""bbb");
var cf2=new CF(111222);
document.write(cf1.CFP1 
+ "
"); //result: CFP1 in Cfp
document.write(cf2.CFP1 + "
"); //result: CFP1 in Cfp
//it will result in a local property in cf1
cf1.CFP1="new value for cf1";
//changes on CF.prototype.CFP1 will affect cf2 but not cf1, because there's already a local property with
//the name CFP1 in cf1, but no such one in cf2
CF.prototype.CFP1="new value for Cfp";
document.write(cf1.CFP1 
+ "
"); //result: new value for cf1
document.write(cf2.CFP1 + "
"); //result: new value for Cfp

Semantic confusion?
Still using the above CF, Cfp example scenario.
According to the mechanism of Prototype, we can say that objects cf1, cf2, etc. all inherit the properties and methods of object Cfp, so it should be said that there is an inheritance relationship between them. The inheritance/sharing of attributes works along the implicit Prototype chain, so the inheritance relationship should also be understood as along this chain.
Let’s look at the instanceOf operation again. Only cf1 instanceOf CF is true. We say that cf1 is an instance object of CF, and CF plays the role of a class. Instead of saying that cf1 is an instance object of Cfp, we should say that cf1 inherits from CF? But CF only acts as a third-party factory, and there is no attribute inheritance relationship between it and cf1.
It is also far-fetched to understand CF and Cfp as a whole.

Prototype is Prototype. There is no need to forcefully combine JavaScript with object-oriented concepts. JavaScript only has limited object-oriented capabilities. From another perspective, we can regard it as a functional language or a dynamic language, so it is A streamlined version that incorporates multiple language features.

Object Model
Where are we?
1. Understand the data types of JavaScript , it is clear that system built-in objects like Number have multiple identities: a) They themselves are a function object, only implemented internally by the engine, b) They represent a data type, and we can use them to define and operate the corresponding type of data , c) The internal implementation mechanism of the engine is hidden behind them, such as internal data structures, various constructors packaged into JavaScript objects, etc.
2. Understand the prototype mechanism, how objects inherit properties and methods through them, and how the prototype relationship is set up inside the JS engine during the object creation process.

After understanding the creation process of the user-defined function object itself, we can have a comprehensive overview of the JavaScript object model.

Function object creation process
When defining a function in JavaScript code, or calling Function to create a function, the Function function will eventually be called in a form similar to this: var newFun=Function(funArgs, funBody); . The main steps to create a function object are as follows:
1. Create a build-in object fn
2. Set the internal [[Prototype]] of fn to Function.prototype
3. Set the internal [[ Call]] attribute, which is an internally implemented method that handles step 3 of the logical reference object creation process
4. Set the internal [[Construct]] attribute, which is an internally implemented method that handles logical reference object creation Steps 1,2,3,4
5 of the process. Set fn.length to funArgs.length, if the function has no parameters, set fn.length to 0
6. Use the same logic with new Object() Create an Object object fnProto
7. Set fnProto.constructor to fn
8. Set fn.prototype to fnProto
9. Return fn
The difference between step 1 and step 6 is that step 1 It just creates the internal data structure (build-in object structure) used to implement the Object object and completes the necessary internal initialization work, but its [[Prototype]], [[Call]], [[Construct]] and other attributes should Is null or an internal initialization value, that is, we can understand it as not pointing to any object (for properties such as [[Prototype]]), or not containing any processing (for properties such as [[Call]], [[Construct]] method). Step 6 will create a new object according to the object creation process described earlier, and its [[Prototype]] etc. will be set.
As can be understood from the above processing steps, any time we define a function, its prototype is an Object instance, so by default when we create instance objects of custom functions, their prototype chains will point to Object.prototype.
In addition, a special feature of Function is that its [[Call]] and [[Construct]] processing logic are the same.

JavaScript Object Model
 
The red dotted line represents the implicit prototype chain.
This object model diagram contains too many things. Many places need to be carefully understood. You can write some test codes for verification. Once you fully understand this picture, your understanding of the JavaScript language is almost the same. Here are some additional instructions:
1. There are several places in the picture where the build-in Function constructor is mentioned. This is the same object and can be tested and verified:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
Function==Function.constructor //result: true
Function==Function.prototype.constructor //result: true
Function==Object.constructor //result: true
//
Function also equals to Number.constructor, String.constructor, Array.constructor, RegExp.constructor, etc.
function fn(){}
Function
==fn.constructor //result: true
This illustrates several issues: Function points to the system's built-in function constructor (build-in Function constructor); Function is bootstrapped; all functions in the system are constructed from Function.

2. The obj1, obj2...objn scope in the lower left corner refers to objects created with code like this: function fn1(){}; var obj1=new fn1();
These objects do not Local constructor methods, but they will get an inherited constructor method from the Prototype chain, that is, fn.prototype.constructor. From the construction process of the function object, we can know that it is fn itself.
The obj1, obj2...objn range in the lower right corner refers to objects created with code like this: var obj1=new Object(); or var obj1={}; or var obj1=new Number(123); or obj1=/w /;etc. Therefore, the pointing of the prototype chain of these objects, the value of the constructor inherited from the prototype chain (referring to whether their constructor is a build-in Number constructor or a build-in Object constructor, etc.) depends on the specific object type. Also note that, var obj=new Object(123); the type of the object created in this way is still Number, which also needs to be determined based on the type of the parameter value.
Similarly, they do not have a local constructor, but obtain the inherited constructor method from the Prototype chain, that is, the build-in *** constructor. The specific one is determined by the data type.

3. Additional explanation about the Prototype chain in the figure:
Object.prototype is the end point of the entire chain, and its internal [[Prototype]] is null.
The Prototype chain of all functions points to Function.prototype.
Function’s Prototype chain points to Function.prototype, which is required by the specification because the designer designed Function to be bootstrapped. After the Function prototype chain is designed in this way, Function.constructor==Function and Function instanceOf Function are both true. In addition, Function is already the top-level constructor, but Function itself is also a function object. It must be created by something, so bootstrapping is semantically reasonable.
The Prototype chain of Function.prototype points to Object.prototype, which is also mandated by the specification. First of all, Function.prototype is an instance object of Function (typeof Function.prototype can know that it is a Function, instanceOf cannot pass the test because the Prototype chain is additionally set internally), so according to the rules of Prototype, the internal function of Function.prototype [ The [Prototype]] value should be the Function.prototype object, that is, its Prototype chain points to itself. On the one hand, this creates an infinite loop in the Prototype chain, and on the other hand, it itself becomes an endpoint. The result is that all function objects will no longer be derived from Object. After adding this mandatory requirement, the prototype chain has only one endpoint.

4. Because Function.prototype is a function object, it should have a displayed prototype attribute, namely Function.prototype.prototype, but it can only be accessed in FireFox and cannot be accessed in IE, Opera, or Safari. Therefore, a symbol indicating non-existence is used in the picture.

5. By default, the [[Prototype]] value of user-defined functions is Object.prototype, that is, its implicit Prototype chain points to Object.prototype, so it is represented in the figure. , but it does not mean that this is always the case. When the user sets the prototype attribute of the custom function, the situation is different.

Execution Model
Introduction to Execution Context
Where JavaScript code runs There is an execution context, which is a concept and a mechanism used to complete the processing of JavaScript runtime scope, lifetime, etc. Execution context includes concepts such as Variable Object, Variable Instatiation, Scope/Scope Chain, etc. There are some differences in processing under different scenarios/execution environments. These scenarios are explained below.

Function objects are divided into user-defined function objects and system built-in function objects. User-defined function objects will be processed according to the mechanism described below, but built-in function objects are related to specific implementations, and ECMA specifications enforce them There are no requirements for the processing of contexts, i.e. they are not generally suitable for what is described in this section.

The executed JavaScript code is divided into three types. The differences in processing of these three types will be explained later:
1. Global Code, that is, global code that is not in any function, such as A js file, js code embedded in an HTML page, etc.
2. Eval Code, which is JS code that is dynamically executed using the eval() function.
3. Function Code, which is the function body JS code in the user-defined function.

Basic Principles
In user-defined functions, you can pass in parameters and define local variables in the function. The function body code can use these input parameters and local variables. What is the mechanism behind it?
When the JS execution flow enters the function, the JavaScript engine creates an object internally, called a Variable Object. Corresponding to each parameter of the function, add an attribute to the Variable Object. The name and value of the attribute are the same as the name and value of the parameter.函数中每声明一个变量,也会在Variable Object上添加一个属性,名字就是变量名,因此为变量赋值就是给Variable Object对应的属性赋值。在函数中访问参数或者局部变量时,就是在variable Object上搜索相应的属性,返回其值。
一般情况下Variable Object是一个内部对象,JS代码中无法直接访问。规范中对其实现方式也不做要求,因此它可能只是引擎内部的一种数据结构。

大致处理方式就这样,但作用域的概念不只这么简单,例如函数体中可以使用全局变量、函数嵌套定义时情况更复杂点。这些情况下怎样处理? JavaScript引擎将不同执行位置上的Variable Object按照规则构建一个链表,在访问一个变量时,先在链表的第一个Variable Object上查找,如果没有找到则继续在第二个Variable Object上查找,直到搜索结束。这就是Scope/Scope Chain的大致概念。

下面是各个方面详细的处理。

Global Object
JavaScript的运行环境都必须存在一个唯一的全局对象-Global Object,例如HTML中的window对象。Global Object是一个宿主对象,除了作为JavaScript运行时的全局容器应具备的职责外,ECMA规范对它没有额外要求。它包Math、 String、Date、parseInt等JavaScript中内置的全局对象、函数(都作为Global Object的属性),还可以包含其它宿主环境需要的一些属性。

Variable Object
上面简述了Variable Object的基本概念。创建Variable Object,将参数、局部变量设置为Variable Object属性的处理过程叫做Variable Instatiation-变量实例化,后面结合Scope Chain再进行详细说明。

Global Code
Variable Object就是Global Object,这是Variable Object唯一特殊的地方(指它是内部的无法访问的对象而言)。
var globalVariable = "WWW";
document.write(window.globalVariable); 
//result: WWW
The above code runs in Global Code mode. According to the processing of Variable Object, when the variable globalVariable is defined, this attribute will be added to the Global Object (i.e. window) object, so the output is the value WWW.

Function Code
Variable Object is also called Activation Object (because there are some differences, so a new name is given in the specification to show the difference. It is called Variable Object in Global Code/Eval Code , it is called Activation Object in Function Code).
Every time you enter function execution, a new Activation Object object will be created, and then an arguments object will be created and set as a property of the Activation Object, and then Variable Instantiation will be processed.
When exiting the function, the Activation Object will be discarded (it is not a memory release, but it can be garbage collected).

Attributes of the arguments object:
length: is the actual number of parameters passed in. Note, referring to the function object creation process, the length on the function object is the number of parameters required when the function is defined;
callee: is the executed function object itself. The purpose is to enable function objects to reference themselves, for example where recursive calls are required.
function fnName(...) { ... } defines the function in this way, and its recursive call can be completed using fnName in the function body. var fn=function(...) { ... } Define the anonymous function in this way. You cannot use the name to refer to yourself in the function body. You can refer to yourself through arguments.callee to implement recursive calls.
Parameter list: The parameter list actually passed in by the caller. This parameter list provides a way to access the actual parameters using indexes. Variable Instantiation will add properties to the Activation Object object during processing, provided that there is a specified parameter list when the function is declared. If the parameter list is not given in the function declaration, or the actual number of parameters is different from the number of parameters in the function declaration, each parameter can be accessed through arguments. The parameter list in

arguments and the parameter attribute on the Activation Object refer to the same parameter object (if modified, it will be reflected in both places). The specification does not require arguments to be an array object, here is a test:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
var argumentsLike = { 0"aaa"12222"WWW", length: 3, callee: function() { } };
document.write(argumentsLike[
2+ "
"); //result: WWW
document.write(argumentsLike[1+ "
"); //result: 222
//
convert the argumentsLike to an Array object, just as we can do this for the arguments property
var array = [].slice.apply(argumentsLike);
document.write(array 
instanceof Array); //result: true
document.write("
");
document.write(array.reverse().join(
"|")); //result: WWW|222|aaa

Eval Code
Variable Object就是调用eval时当前执行上下文中的Variable Object。在Global Code中调用eval函数,它的Variable Object就是Global Object;在函数中调用eval,它的Variable Object就是函数的Activation Object。
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
function fn(arg){
    
var innerVar = "variable in function";
    eval(
' \
        var evalVar = "variable in eval"; \
        document.write(arg + "
"); \
        document.write(innerVar + "
"); \
    
');
    document.write(evalVar);
}
fn(
"arguments for function");
输出结果是:
arguments for function
variable in function
variable in eval
说明: eval调用中可以访问函数fn的参数、局部变量;在eval中定义的局部变量在函数fn中也可以访问,因为它们的Varible Object是同一个对象。

Scope/Scope Chain
首先Scope Chain是一个类似链表/堆栈的结构,里面每个元素基本都是Variable Object/Activation Object。
其次存在执行上下文的地方都有当前Scope Chain,可以理解为Scope Chain就是执行上下文的具体表现形式。

Global Code
Scope Chain只包含一个对象,即Global Object。在开始JavaScript代码的执行之前,引擎会创建好这个Scope Chain结构。

Function Code
函数对象在内部都有一个[[Scope]]属性,用来记录该函数所处位置的Scope Chain。
创建函数对象时,引擎会将当前执行环境的Scope Chain传给Function的[[Construct]]方法。[[Construct]]会创建一个新的Scope Chain,内容与传入的Scope Chain完全一样,并赋给被创建函数的内部[[Scope]]属性。在前面函数对象创建过程一节中,这个处理位于步骤4和5之间。
进入函数调用时,也会创建一个新的Scope Chain,包括同一个函数的递归调用,退出函数时这个Scope Chain被丢弃。新建的Scope Chain第一个对象是Activation Object,接下来的内容与内部[[Scope]]上存储的Scope Chain内容完全一样。

Eval Code
进入Eval Code执行时会创建一个新的Scope Chain,内容与当前执行上下文的Scope Chain完全一样。

实例说明
Scope Chain的原理就上面这些,必须结合JS代码的执行、Variable Instantiation的细节处理,才能理解上面这些如何产生作用,下面用一个简单的场景来综合说明。假设下面是一段JavaScript的Global Code:
var outerVar1="variable in global code";
function fn1(arg1, arg2){
    
var innerVar1="variable in function code";
    
function fn2() { return outerVar1+" - "+innerVar1+" - "+" - "+(arg1 + arg2); }
    
return fn2();
}
var outerVar2=fn1(1020);
执行处理过程大致如下:
1. 初始化Global Object即windo0,0)">20);
The execution process is roughly as follows:
1. Initialize the Global Object, which is the window object, and the Variable Object, which is the window object itself. Create a Scope Chain object, assuming it is scope_1, which only contains the window object.
2. Scan the JS source code (read the source code, there may be a lexical and syntax analysis process), and you can get the defined variable names and function objects from the results. According to the scanning order:
2.1 Discover the variable outerVar1, add the outerVar1 attribute to the window object, the value is undefined;
2.2 Discover the definition of function fn1, use this definition to create a function object, and the Scope Chain passed to the creation process is scope_1 . Add the result to the window property, with the name fn1 and the value being the returned function object. Note that the internal [[Scope]] of fn1 is scope_1. Also note that the creation process does not perform special processing on the JS code in the function body. It can be understood that it only saves the scanning results of the JS code in the function body in the internal properties of the function object, and further processes it when the function is executed. This is key to understanding Function Code, especially Variable Instantiation in nested function definitions;
2.3 Discover the variable outerVar2, add the outerVar2 attribute to the window object, and the value is undefined;
3. Execute the outerVar1 assignment statement and assign the value is "variable in global code".
4. Execute function fn1 and get the return value:
4.1 Create an Activation Object, assuming it is activation_1; create a new Scope Chain, assuming it is scope_2, the first object in scope_2 is activation_1, and the second object is window object (taken from [[Scope]] of fn1, that is, the content in scope_1);
4.2 Process parameter list. Set the attributes arg1 and arg2 on activation_1 with values ​​10 and 20 respectively. Create arguments object and set it, set arguments as attributes of activation_1;
4.3 Perform a process similar to step 2 on the function body of fn1:
4.3.1 Discover the variable innerVar1, add the innerVar1 attribute on the activation_1 object, The value is undefine;
4.3.2 Find the definition of function fn2, use this definition to create a function object, and the Scope Chain passed to the creation process is scope_2 (the Scope Chain of function fn1 is the content of the current execution context). Add the result to the attribute of activation_1, with the name fn2 and the value being the returned function object. Note that the internal [[Scope]] of fn2 is scope_2;
4.4 Execute the innerVar1 assignment statement and assign the value to "variable in function code".
4.5 Execute fn2:
4.5.1 Create an Activation Object, assuming it is activation_2; create a new Scope Chain, assuming it is scope_3, the first object in scope_3 is activation_2, and the following objects are activation_1, window Object (taken from [[Scope]] of fn2, that is, scope_2);
4.5.2 Process parameter list. Because fn2 has no parameters, just create the arguments object and set it as the attribute of activation_2.
4.5.3 Perform a process similar to step 2 on the function body of fn2, and no variable definitions and function declarations are found.
4.5.4 Execute function body. For any variable reference, search from scope_3. In this example, outerVar1 will be found on window; innerVar1, arg1, and arg2 will be found on activation_1.
4.5.5 Discard scope_3 and activation_2 (meaning they can be garbage collected).
4.5.6 Return the return value of fn2.
4.6 Discard activation_1 and scope_2.
4.7 Return results.
5. Assign the result to outerVar2.

In other cases, Scope Chain and Variable Instantiation can be analyzed similar to the above process.

According to the above facts
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn