Suitable readers: experienced developers and professional front-end personnel.
Author: Dmitry A. Soshnikov
Release date: 2010-09-02
Original article: http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
1: http://ued.ctrip.com/blog? P = 2795
Reference 2: http://www.cnblogs.com/ifishing/archive/2010/12/08/1900594.html
It mainly integrates the Chinese translation of the above two masters and combines the essence of the two articles.
First, let's take a look at the concept of Object [Object], which is also the most basic concept in ECMASript.
Object
ECMAScript is a highly abstract object-oriented (object-oriented) language used to process Objects. Of course, there are also basic types, but it also needs to be converted to object Objects when necessary.
An object is a collection of properties and has a single prototype object. The prototype may be either an object or the null value.
An Object is a set of attributes and has a separate prototype object [prototype object]. This prototype object [prototype Object] can be an object or a null value.
Copy code
Let's take an example of a basic Object. First, we need to know that the prototype of an Object is a reference to the internal [prototype] attribute.
However, in general, we use __< internal attribute name >__ to replace double parentheses, for example, _ proto _ (this is the implementation of prototype concepts by some script engines such as SpiderMonkey, although not the standard ).
Copy codeThe Code is as follows:
Var foo = {
X: 10,
Y: 20
};
The above code foo object has two explicit attributes: [explicit own properties] and an implicit _ proto _ attribute [implicit _ proto _ property]. the prototype pointing to foo.
Figure 1. A basic object with a prototype
Why is prototype required? Let's consider the concept of prototype chain to answer this question.
Prototype chain)
A prototype object is also a common object and may have its own prototype. If the prototype of a prototype object is not null, we call it a prototype chain ).
A prototype chain is a finite chain of objects which is used to implemented inheritance and shared properties. prototype chain is A finite object chain composed of objects due to inheritance and shared attributes.
Imagine a situation where most of the two objects are the same and only a small part is different. Obviously, in a good design model, we need to reuse the same part, instead of repeatedly defining the same methods or attributes in each object. In A system based on class [class-based], these reusable parts are called class inheritance-put the same part into class A, and then class B and class C inherit from class, you can declare that you have something unique.
ECMAScript does not have the concept of a class. However, the concept of reusing [reuse] is no different (in some aspects, or even more flexible than class-) and can be implemented by prototype chain. This inheritance is called delegation based inheritance-based delegation, or more commonly, prototype inheritance.
Similar to "A", "B", and "C", create an object class "a", "B", and "c" in ECMAScript. Correspondingly, the object "a" has a common part of the object "B" and "c. Meanwhile, objects "B" and "c" only contain their own additional attributes or methods.
Copy codeThe Code is as follows:
Var a = {x: 10, calculate: function (z) {return this. x + this. y + z }}; var B = {y: 20, _ proto __: a}; var c = {y: 30, _ proto __: }; // call the inherited method B. calculate (30); // 60c. calculate (40); // 80
It seems very easy. B and c can use the calculate method defined in a, which is implemented by the original type chain [prototype chain.
The principle is simple: if the calculate method cannot be found in object B (that is, the calculate attribute is not found in object B), it will be searched along the prototype chain. If the calculate method is not found in prototype of B, the prototype of a is found along the prototype chain and the entire prototype chain is traversed. Remember, once found, the first property or method is returned. Therefore, the first property is an inherited property. If the entire prototype chain is still not found, undefined is returned.
Note that this value still points to the object it originally belongs to in an inheritance mechanism, rather than the object it belongs to when it is found on the prototype chain. For example, in the above example, this. y is obtained from B and c, not. Of course, you also found that this. x is obtained from a, because it is found through the prototype chain mechanism.
If the prototype of an object has not been declared or defined, the default value of _ prototype _ is object. prototype, and object. prototype also has a _ prototype __, which is the end point of the prototype chain and is set to null.
The following figure shows the inheritance relationships of a, B, and c.
Figure 2. prototype chain
A prototype chain is usually used in this case: the object has the same or similar state structure (same or similar state structure) (that is, the same property set) different Status values (different state values ). In this case, we can use Constructor to create an object in a specific mode (specified pattern.
Constructor)
In addition to creating objects, constructor has also done another useful thing: prototype object is automatically set for the newly created object ). The prototype object is stored in the ConstructorFunction. prototype attribute.
For example, if we rewrite the previous example and use the constructor to create objects "B" and "c", then object "a" assumes the role "Foo. prototype:
Copy codeThe Code is as follows:
// Constructor
Function Foo (y ){
// The constructor creates an object in a specific mode. The created object has the "y" attribute.
This. y = y;
}
// "Foo. prototype" stores the prototype reference of the new object.
// So we can use it to define inheritance and sharing attributes or methods.
// So, like the previous example, we have the following code:
// Inherit the property "x"
Foo. prototype. x = 10;
// Inheritance Method "calculate"
Foo. prototype. calculate = function (z ){
Return this. x + this. y + z;
};
// Create "B" and "c" in foo Mode"
Var B = new Foo (20 );
Var c = new Foo (30 );
// Call the inherited method
B. calculate (30); // 60
C. calculate (40); // 80
// Let's see if the expected attributes are used
Console. log (
B. _ proto _ = Foo. prototype, // true
C. _ proto _ = Foo. prototype, // true
// "Foo. prototype" automatically creates a special attribute "constructor"
// Point to the constructor itself of
// Instance "B" and "c" can be found through authorization and used to detect their own Constructor
B. constructor === Foo, // true
C. constructor === Foo, // true
Foo. prototype. constructor === Foo // true
B. calculate = B. _ proto _. calculate, // true
B. _ proto _. calculate = Foo. prototype. calculate // true
);
The above code can be expressed as the following link:
Figure 3. Relationships between constructors and objects
The preceding figure shows that each object has a prototype. the constructor Foo also has its own _ proto __, that is, Function. prototype, while Function. prototype _ proto _ points to Object. prototype. reapply, Foo. prototype is only an explicit attribute, that is, the _ proto _ attribute of B and c.
The complete and detailed explanation of this problem can be found in Chapter 2 and Chapter 19 of uncle's upcoming translation. There are two parts: object-oriented programming. general Theory (OOP. the general theory describes different object-oriented paradigms and stylistics, as well as comparison with ECMAScript and object-oriented programming. ECMAScript implementation (OOP. ECMAScript implementation) specifically describes Object-Oriented Programming in ECMAScript.
Now that we know the basic object principle, let's take a look at the program execution environment [runtime program execution] In ECMAScript. this is commonly referred to as "execution context stack" [execution context stack]. Each element can be abstracted as an object. You may have discovered that, right. In ECMAScript, You can see objects almost everywhere.
Execution Context Stack)
There are three types of code in ECMASscript: global, function, and eval.
The execution of each type of code depends on its own context. Of course, the global context may cover a lot of function and eval instances. Every call to a function enters the context of the function execution and calculates the values of variables in the function. Each execution of the eval function also enters the context of the eval execution to determine where to obtain the value of the variable.
Note that a function may generate an infinite context, because a function call (or even recursion) produces a new context.
Copy codeThe Code is as follows:
Function foo (bar ){}
// Call the same function. Three different contexts are generated each time.
// (Contains different states, such as the value of the bar parameter)
Foo (10 );
Foo (20 );
Foo (30 );
An execution context can activate another context, just like a function calls another function (or a global context calls a global function), and then calls the function layer by layer. Logically speaking, this implementation method is stack, which we can call as the context stack.
A context that activates other contexts is called a caller ). The activated context is called callee ). The caller may also be the caller (for example, a function called in a global context calls some internal methods ).
When a caller activates a callee, the caller suspends its own execution and gives control to the callee. therefore, this callee is put into the stack and is called the context in progress [running/active execution context]. after the callee context ends, the control is handed over to its caller again, and then the caller will continue to execute in the paused place. After the caller ends, other contexts will be triggered. A callee can end its context by returning or throwing an exception.
For example, the execution of all ECMAScript programs can be considered as an execution context stack [execution context (EC) stack]. The top of the stack is the active context.
Figure 4. Execution context Stack
When a program starts, it first enters the global execution context Environment [global execution context], which is also the lowest element in the stack. The global program starts initialization to generate the necessary objects [objects] and functions [functions]. in the global context execution process, it may activate some methods (of course initialized), then enter their context environment, and then push new elements into the stack. After these initialization ends, the system will wait for some events (such as users' mouse clicks), trigger some methods, and then enter a new context.
As shown in figure 5, there is a function context "EC1" and a Global context "Global EC", showing the stack changes when "Global EC" enters and exits "EC1:
Figure 5. Change of execution context Stack
When ECMAScript is running, the system manages code execution in this way.
For details about the execution context stack of ECMAScript, refer toChapter 2 Execution context).
As mentioned above, each execution context in the stack can be expressed as an object. Let's take a look at the structure of the context object and the state required to execute its code ).
Execution Context)
The context of an execution can be abstracted as an object. Each execution context has a series of attributes (called context state) that are used to track the execution progress of associated code. This graph is a context structure.
Figure 6. Context Structure
In addition to the three required attributes (variable object), this pointer (this value), scope chain )), the execution context can also have any additional attributes based on the specific implementation. Next, let's take a closer look at these three attributes.
Variable Object)
A variable object is a scope of data related with the execution context.
It's a special object associated with the context and which stores variables and function declarations are being defined within the context.
Variable object is the scope of data related to the execution context ).
It is a special object associated with the context, used to store variables and function declarations defined in the context ).
Copy code
Note: The function expression [function expression] (instead of the function declaration [function declarations, see Chapter 2nd of this series) is not included in VO [variable object.
Variable Object is an abstract concept. Different contexts use different objects. For example, in the global context, the variable object is also the global object itself [global object]. (This means that we can point to global variables through the Global Object Attributes ).
Let's take a look at the global execution context in the following example:
Copy codeThe Code is as follows:
Var foo = 10;
Function bar () {}/// function declaration
(Function baz () {}); // function expression
Console. log (
This. foo = foo, // true
Window. bar = bar // true
);
Console. log (baz); // reference error. baz is not defined
The variable object (VO) in the global context has the following attributes:
Figure 7. global variable object
As shown above, the function "baz" is not included in variable objects if it is used as a function expression. This is the reason why a ReferenceError occurs when a function tries to access the service. Note that, compared with other languages (such as C/C ++), ECMAScript only allows functions to create new scopes. Variables and internal functions defined in the function are not directly visible outside and do not pollute global objects. When using eval, we will also use a new (eval creation) Execution context. Eval uses the global variable object or the caller's variable object (the call source of eval ).
What is the function and its own variable object? In a function context, a variable object is represented as an activity object ).
Activation object)
When the function is activated by the caller, this special activity object is created. It contains common parameters and special parameters (arguments) objects (parameter ing tables with index attributes ). Activity objects are used as variable objects in the context of functions.
That is, the variable object of the function remains unchanged, but besides the stored variables and function declaration, it also contains the special object arguments.
Consider the following:
Copy codeThe Code is as follows:
Function foo (x, y ){
Var z = 30;
Function bar () {}// function declaration
(Function baz () {}); // function expression
}
Foo (10, 20 );
Shows the next activation object (AO) in the context of the "foo" function:
Figure 8. Activation object
In the same way, function expressions are not in the ranks of AO.
For details about this AO, refer to Chapter 9th of this series of tutorials.
Next we will talk about the third main object. As we all know, in ECMAScript, we will use the internal function [inner functions]. In these internal functions, we may reference its parent function variable or global variable. We turn these variable objects into the context scope object [scope object of the context]. Similar to the prototype chain [prototype chain] discussed above, we are called the scope chain [scope chain] Here.
Scope chain)
A scope chain is a list of objects that are searched for identifiers appear in the code of the context.
A scope chain is a list of objects used to retrieve identifiers in the context code ).
Copy code
The principle of the scope chain is similar to that of the prototype chain. If this variable does not exist in its own scope, it will look for the parent level until the top level.
The identifier [Identifiers] can be understood as the variable name, function declaration, and common parameters. For example, if a function needs to reference a variable in its own function, but the variable is not declared in the function (or is not a parameter name ), this variable can be called the free variable [free variable]. So we need to use the scope chain to search for these free variables.
In general, a scope chain includes a variable object (the top of the scope chain), a function's own variable VO, and an activity object (activation object ). However, in some cases, it also contains other objects, such as dynamically joining the with or catch statement in the scope chain during execution. [Note: with-objects refers to the with statement, which produces a temporary scope object. catch-clses refers to catch clauses, such as catch (e), which produces an exception object, cause Scope change].
When looking for an identifier, it will start from the activity object part Of the scope chain, and then (if the identifier is not found in the activity object) it will find the top of the scope chain, round-robin, just like the scope chain.
Copy codeThe Code is as follows:
Var x = 10;
(Function foo (){
Var y = 20;
(Function bar (){
Var z = 30;
// "X" and "y" are free Variables
// It will be found in the next object of the scope chain (after the interaction object of the function "bar)
Console. log (x + y + z );
})();
})();
Let's assume that the objects in the scope chain are linked through an attribute called _ parent _, which is the next object pointing to the scope chain. This can be tested in the Rhino Code. This technology is indeed implemented in the ES5 environment (there is an outer link ). of course, you can also use a simple data to simulate this model. Using the concept of _ parent _, we can demonstrate the above Code as follows. (Therefore, the parent variable is included in the [[Scope] attribute of the function ).
Figure 9. Scope chain
During code execution, if the with or catch statement is used, the scope chain is changed. These objects are simple objects, and they also have prototype chains. In this way, the scope chain will be searched from two dimensions.
- First, in the original scope chain
- The scope chain of each link point (if this link point has prototype)
Let's look at the following example:
Copy codeThe Code is as follows:
Object. prototype. x = 10;
Var w = 20;
Var y = 30;
// In the SpiderMonkey Global Object
// For example, the global context variable Object is inherited from "Object. prototype"
// So we can get "no declared global variables"
// Because it can be obtained from the prototype chain
Console. log (x); // 10
(Function foo (){
// "Foo" is a local variable.
Var w = 40;
Var x = 100;
// "X" can be obtained from "Object. prototype". Note that the value is 10.
// Because {z: 50} is inherited from it
With ({z: 50 }){
Console. log (w, x, y, z); // 40, 10, 30, 50
}
// After the "with" object is deleted from the scope chain
// X can be obtained from the context of foo again. Note that this value returns to 100 again.
// "W" is also a local variable
Console. log (x, w); // 100, 40
// In the browser
// We can use the following statement to obtain the global w value.
Console. log (window. w); // 20
})();
The following figure shows the structure. This means that before we search for _ parent _, we will first go to the _ proto _ link.
Figure 10. with extended scope chain
Note that not all global objects are inherited by Object. prototype. The figure above can be tested in SpiderMonkey.
As long as the variable objects of all external functions exist, there is nothing special about referencing external data from internal functions-we only need to traverse the scope linked list to find the required variables. However, as mentioned above, when a context is terminated, its state and itself will be destroyed (destroyed), and the internal function will return from the external function. In addition, the returned function may be activated in other contexts. What if a previously terminated context containing some free variables is activated? Generally, the concept of solving this problem is directly related to the scope chain in ECMAScript, known as the (lexical) closure ).
Closure (Closures)
In ECMAScript, a function is the first class object. This term means that the function can be passed as a parameter to other functions (in this case, the function is called "funargs" -- the abbreviation of "functional arguments: I do not know whether the translation is a functional parameter]). A function that receives "funargs" is called a higher-order function, or an operator that is closer to a mathematical concept ). Other functions also return functions when they are running. These returned functions are called function valued functions (functions with functional values ).
"Funargs" and "functional values" have two conceptual problems. These two subproblems are called "Funarg problem" ("functional parameter problem "). To accurately solve the functional parameter problem, we need to introduce the concept of closure (closures. Let's describe these two problems carefully (we can see that the [[Scope] attribute of the function is used in ECMAScript to solve this problem ).
One sub-problem of "funarg problem" is "upward funarg problem". This problem occurs when a function is returned from another function to the outside. The variable that enters the external context at the end of the external context. When an internal function is created (at creation moment) you need to store it in the Scope of the parent element of the [[Scope] attribute. Then, when the function is activated, the context Scope chain is expressed as a combination of the activation object and [[Scope] attributes (in fact, we can see it ):
Scope chain = Activation object + [[Scope]
Scope chain = activity object + [Scope]
Note that the most important thing is that the function saves the external scope when it is created because the saved scope chain (saved scope chain) it will be used for Variable Search in future function calls.
Copy codeThe Code is as follows:
Function foo (){
Var x = 10;
Return function bar (){
Console. log (x );
};
}
// "Foo" returns a function
// And the returned function can use the internal variable x at will.
Var returnedFunction = foo ();
// Global variable "x"
Var x = 20;
// Supports the returned function
ReturnedFunction (); // The result is 10 instead of 20.
This form of scope is called static scope [static/lexical scope]. The preceding x variable is found in [[Scope] of the function bar. In theory, there will also be a dynamic scope [dynamic scope], that is, the above x is interpreted as 20, rather than 10. But EMCAScript does not use a dynamic scope.
Another type of "funarg problem" is top-down ["downward funarg problem"]. In this case, parent-level upper and lower will exist, but there will be ambiguity when judging a variable value. That is, the scope of the variable. Is it the scope when the function is created, or the scope when the function is executed? To avoid this ambiguity, you can use closures, that is, static scopes.
See the following example:
Copy codeThe Code is as follows:
// Global variable "x"
Var x = 10;
// Global function
Function foo (){
Console. log (x );
}
(Function (funArg ){
// Local variable "x"
Var x = 20;
// This Is Not ambiguous.
// Because we use the global variable "x" saved in [Scope] of the "foo" function ",
// It is not the "x" of the caller Scope"
FunArg (); // 10 instead of 20
}) (Foo); // pass foo as a "funarg"
From the above situation, we seem to be able to conclude that using static scopes in languages is a mandatory requirement of closures. However, in some languages, dynamic and static scopes can be combined to allow developers to select which scopes. However, in ECMAScript, only static scopes are used. Therefore, ECMAScript fully supports the [[Scope] attribute. We can define the closure as follows:
A closure is a combination of a code block (in ECMAScript this is a function) and statically/lexically saved all parent scopes.
Thus, via these saved scopes a function may easily refer free variables.
A closure is a series of code blocks (functions in ECMAScript) and stores all parent scopes statically. Use these saved scopes to search for free variables in the function.
Copy code
Note that every common function saves [[Scope] when it is created. Theoretically, all functions in ECMAScript are closures.
Another important point is that several functions may contain the same parent-level scopes (this is a common situation, such as several internal or global functions ). In this case, variables in [Scope] will be shared. Variable changes in one closure also affect the other closure.
Copy codeThe Code is as follows:
Function baz (){
Var x = 1;
Return {
Foo: function foo () {return ++ x ;},
Bar: function bar () {return -- x ;}
};
}
Var closures = baz ();
Console. log (
Closures. foo (), // 2
Closures. bar () // 1
);
The above code can be expressed in this figure:
Figure 11. Shared [Scope]
Creating multiple functions in a loop can cause confusion. If you use cyclic variables (such as "k") in the created functions, all the functions use the same cyclic variables, causing some programmers to frequently fail to get the expected values. It is clear why this problem occurs-because all functions share the same [Scope], where the cyclic variable is the last re-assigned value.
Copy codeThe Code is as follows:
Var data = [];
For (var k = 0; k <3; k ++ ){
Data [k] = function (){
Alert (k );
};
}
Data [0] (); // 3, but not 0
Data [1] (); // 3, but not 1
Data [2] (); // 3, but not 2
There are some technologies used to solve such problems. One of the tricks is to provide an additional object in the scope chain, such as adding a function:
Copy codeThe Code is as follows:
Var data = [];
For (var k = 0; k <3; k ++ ){
Data [k] = (function (x ){
Return function (){
Alert (x );
};
}) (K); // pass k as a parameter
}
// The result is correct.
Data [0] (); // 0
Data [1] (); // 1
Data [2] (); // 2
The in-depth study and practice of closure theory can be found in Chapter 1 closure (Closures) of this series of tutorials. For more information about the Scope chain, see Chapter 1 Scope chain in this series of tutorials ).
The next chapter will discuss the concept of the last attribute of an execution context-this pointer.
This pointer
A this value is a special object which is related with the execution context.
Therefore, it may be named as a context object (I. e. an object in which context the execution context is activated ).
This is a special object that is closely related to the context of execution. Therefore, it can also be called the context object [context object] (which activates the context of the execution context ).
Copy code
Any object can be used as the context's this value. I would like to clarify again some of the descriptions related to the execution context in ECMAScript-especially the misunderstanding of this. Generally, this is incorrectly described as the property of the variable object. Recently, for example, I found out in this book (although the chapter mentioned in this book is not bad ). Remember:
A this value is a property of the execution context, but not a property of the variable object.
This is an attribute of the execution context, not a property of a variable object.
Copy code
This feature is important because, unlike variables, this does not have a process similar to searching for variables. When you use this in the Code, the value of this is obtained directly from the context of the execution, rather than from the scope chain. The value of this depends only on the context.
By the way, unlike ECMAScript, Python has a self parameter, which is similar to this, but can be changed during execution. In ECMAScript, the value of this cannot be assigned, because, in other words, this is not a variable.
In global context, the value of this refers to the global object, which means that the value of this is the variable itself.
Copy codeThe Code is as follows:
Var x = 10;
Console. log (
X, // 10
This. x, // 10
Window. x // 10
);
In the function context [function context], this may become different values based on each function call. this is provided by each caller. caller is generated by calling the [call expression] (that is, how this function is activated and called ). For example, in the following example, foo is a callee and is activated in a global context. The following example shows the difference of this caused by different caller.
Copy codeThe Code is as follows:
// The alert in the "foo" function has not changed
// But this is different each time the call is activated.
Function foo (){
Alert (this );
}
// Caller activates the callee "foo,
// And provide "this" for this callee
Foo (); // Global Object
Foo. prototype. constructor (); // foo. prototype
Var bar = {
Baz: foo
};
Bar. baz (); // bar
(Bar. baz) (); // also bar
(Bar. baz = bar. baz) (); // This is a global object.
(Bar. baz, bar. baz) (); // It is also a global object.
(False | bar. baz) (); // It is also a global object.
Var otherFoo = bar. baz;
OtherFoo (); // still a Global Object
If you need to think deeply about the change of this value in each callback function call (more importantly, how it changes), you can read This chapter in this series of tutorials. The situations mentioned above will be discussed in detail in this chapter.
Conclusion)
Here we have completed a brief overview. Although it may not be so short, a whole book is required to complete these topics .. We have not mentioned two important topics: functions (functions) (and differences between different types of functions, such as function declaration and function expressions) and evaluation strategy (ECMAScript ). For more information about these two topics, see Chapter 1 Functions and Chapter 2 Evaluation strategy ).
If you have any comments, questions, or comments, I welcome them to be discussed in the comments.
I wish you all the best at learning ECMAScript.