This article is an overview of the Ecma-262-3 in detail series (I will translate these articles into this series (第11-19 chapter) later. Each chapter has a more detailed link to the content, and you can continue to read the detailed links for each section for a deeper understanding.
Suitable readers: experienced developers, professional front-end personnel.
Original author: Dmitry A. Soshnikov
Date: 2010-09-02
Original: Http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
Reference 1:http://ued.ctrip.com/blog/?p=2795
Reference 2:http://www.cnblogs.com/ifishing/archive/2010/12/08/1900594.html
The main combination of the above 2 masters of the Chinese translation, the two pieces of the essence of the article are combined together.
Let's first look at the concept of object, which is the most basic concept in ecmasript.
Objects Object
ECMAScript is a highly abstract object-oriented (object-oriented) language that handles objects objects. Of course, there are basic types, but they also need to be converted to object objects when necessary.
An object is a collection of properties and have a single prototype object. The prototype is either an object or the null value.
Object is a collection of properties and has a separate prototype object [prototype object]. This prototype object [prototype-object] can be either an object or a null value.
To give an example of a basic object, let's first make it clear that the prototype of an object is a reference to an internal [[prototype]] property.
In general, however, we use the __< internal attribute name >__ underscore instead of the double parenthesis, such as __proto__ (which is a specific implementation of the prototype concept for some scripting engines such as SpiderMonkey, although not a standard).
var foo = {
X:10,
Y:20
};
The above code Foo object has two explicit properties [explicit own properties] and an implicit __proto__ attribute [implicit __proto__ property], which points to the prototype of Foo.
Figure 1. A basic object that contains a prototype
Why we need prototypes, let's consider the concept of the prototype chain to answer this question.
Prototype chain (Prototype chain)
The prototype object is also an ordinary object, and it is possible to have its own prototype, if the prototype of a prototype object is not NULL, we call it the prototype chain (prototype chain).
A prototype chain is a finite chain of objects which are used to implemented inheritance and shared properties.
A prototype chain is a finite object chain composed of objects that implement inheritance and share properties.
Imagine a situation where 2 objects, most of them are the same, and only a few are different, it is clear that in a good design pattern we will need to reuse that same part rather than repeating the same methods or attributes in each object. In systems based on class [class-based], these reusable parts are called inheritance of classes-the same parts are placed in Class A, then Class B and Class C inherit from A, and can declare something unique.
ECMAScript does not have a class concept. However, the idea of reusing [reuse] is no different (in some ways, even more flexible than class-) and can be implemented by the prototype chain prototype chain. This inheritance is referred to as delegation based inheritance-inheritance-based delegates, or more commonly called prototype inheritance.
Similar to the class "A", "B", "C", in ECMAScript Create the object class "A", "B", "C", and accordingly, the object "A" has the common parts of the object "B" and "C". Both the objects "B" and "C" contain only their own attached properties or methods.
var a = {
X:10,
Calculate:function (z) {
Return this.x + This.y + Z
}
};
var B = {
Y:20,
__PROTO__: A
};
var C = {
Y:30,
__PROTO__: A
};
Call the inherited method.
B.calculate (30); 60
C.calculate (40); 80
Does that look very simple? B and C can use the Calculate method defined in a, which is implemented with a prototype chain [prototype chain].
The principle is simple: if you cannot find the Calculate method in object B (that is, the Calculate attribute is not in object B), you will start looking along the prototype chain. If the Calculate method is not found in the prototype of B, then a prototype is found along the prototype chain, and the entire prototype chain is traversed. Remember, once found, returns the first property or method found. Therefore, the first found property becomes an inheritance property. If the entire prototype chain is traversed, it is still not found, then the undefined is returned.
Note that this value is in an inheritance mechanism and is still the object that it belongs to when it belongs to the object that it originally belonged to, not when it was found on the prototype chain. For example, in the example above, THIS.Y is obtained from B and C, not a. Of course, you also find that this.x is taken from a, because it is found through the prototype chain mechanism.
If the prototype of an object is not declared or defined, then the default value of __prototype__ is Object.prototype, and Object.prototype has a __prototype__, This is the end of the prototype chain and is set to null.
The following illustration shows the inheritance relationship of the above a,b,c
Figure 2. Prototype chain
The prototype chain will typically be used in situations where an object has the same or similar state structure (same or similar-structure) (that is, the same set of properties) with a different state value (different the values). In this case, we can use the constructor (Constructor) to create the object under a specific pattern (specified pattern).
Constructors (Constructor)
In addition to creating objects, the constructor (constructor) also does another useful thing--automatically sets the prototype object (prototype objects) for the new object that was created. The prototype object is stored in the Constructorfunction.prototype property.
For example, if we rewrite the previous example, using constructors to create objects "B" and "C", then object "A" plays the role of "Foo.prototype":
constructor function
function Foo (y) {
The constructor will create the object in a specific pattern: the object being created will have a "Y" property
This.y = y;
}
"Foo.prototype" contains a prototype reference for a new object
So we can use it to define inheritance and shared properties or methods
So, as in the example above, we have the following code:
Inheritance property "X"
foo.prototype.x = 10;
Inheritance method "Calculate"
Foo.prototype.calculate = function (z) {
Return this.x + This.y + z;
};
Create "B" and "C" using 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 we've used the expected properties
Console.log (
b.__proto__ = = = Foo.prototype,//True
c.__proto__ = = = Foo.prototype,//True
"Foo.prototype" automatically creates a special attribute "constructor"
The constructor itself that points to a
Instance "B" and "C" can be found by authorization and used to detect their own constructors
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 relationship:
Figure 3. The relationship between constructors and objects
As you can see from the above diagram, each object has a prototype. The constructor Foo also has its own __proto__, which is Function.prototype, and Function.prototype's __proto__ points to Object.prototype. Again, Foo.prototype is just an explicit attribute, that is, the __proto__ property of B and C.
A complete and detailed explanation of the problem can be found in the 18th and 192 chapters of the uncle's forthcoming translation. There are two parts: Object-oriented Programming. General Theory (OOP. The general theory), describes the different object-oriented paradigms and styles (OOP Paradigms and Stylistics), and the comparison with ECMAScript, object-oriented programming. ECMAScript implementation (OOP. ECMAScript implementation), which specifically describes object-oriented programming in ECMAScript.
Now that we've learned the basic object principle, let's take a look at the program execution environment in ECMAScript [runtime programs execution]. This is what is commonly referred to as the "execution context stack" [execution context Stack]. Each element can be abstractly understood as an object. You may have found out, yes, in the ECMAScript, almost everywhere you can see the figure of object.
Execution context stack (execution context stack)
There are three types of code in Ecmasscript: Global, Function, and eval.
The execution of each code needs to depend on its own context. Of course global context may cover a lot of function and eval instances. Each invocation of a function enters the context of the function execution and computes the value of the variable in the function. Each execution of the Eval function also enters the context of the eval execution and determines where the value of the variable should be obtained.
Note that a function can produce an infinite context, because the invocation (or even recursion) of one of the functions produces a new context.
function foo (bar) {}
Invokes the same function, producing 3 different contexts at a time
(contains a different state, such as the value of the parameter bar)
Foo (10);
Foo (20);
Foo (30);
An execution context can activate another context, just as a function calls another function (or a global context invokes a global function) and then calls down one layer at a level. Logically, this implementation is the stack, which we can call the context stack.
A context that activates another context is called the caller (caller). The context being activated is called the callee (callee). The callee may also be the caller (such as a function called in the global context that calls some of its own internal methods).
When a caller activates a callee, the caller pauses its own execution and then gives control to the callee. The callee is then put into the stack, called the In-progress context [running/active execution context]. When the context of this callee is finished, control is handed over to its caller again, and caller will continue to execute where it was just paused. At the end of this caller, the other context will continue to be triggered. A callee can end its own context by returning (return) or throwing an exception (exception).
For example, all ECMAScript program execution can be seen as an execution context stack [execution context (EC) stacks]. The top of the stack is the context in which the activation state is active.
Figure 4. Execution context Stack
When a program starts, it enters the global execution context [global execution context], which is also the bottommost element in the stack. This global program starts initializing, initializing the necessary objects [objects] and functions [functions]. During this global context execution, it may activate some methods (which are, of course, already initialized), then enter their context and then press the new element onto the stack. After the initialization is complete, the system waits for events (such as a user's mouse click, etc.), triggering some methods and then entering a new context.
As shown in Figure 5, there is a function context of "Ec1″" and a global Context "global EC", showing the changes in the stack when entering and exiting from "Global EC" Ec1″:
Figure 5. Change of execution context stack
This is how the ECMAScript runtime system manages code execution.
For the contents of the ECMAScript execution context stack, refer to the 11th Execution context (execution context) of this series of tutorials.
As mentioned above, each execution context in the stack can be represented as an object. Let's look at the structure of the context object and the state it needs to execute its code.
Execution contexts (execution context)
The context of an execution can be abstracted as an object. Each execution context has a series of attributes (which we call the context state) that they use to track the progress of the execution of the associated code. This diagram is the structure of a context.
Figure 6. Context structure
In addition to these 3 required attributes (variable objects (variable object), this pointer (this value), scope chain (scope chain)), the execution context can also have any additional properties depending on the implementation. Next, let's take a closer look at these three properties.
Variable objects (Variable object)
It s a special object associated with the context and which stores variables and function declarations is being defined W Ithin the context.
The Variable object (variable object) is the data scope that is related to the execution context (scope of data).
It is a special object associated with the context that stores variables (variables) and function declarations (functions declarations) that are defined in the context.
Note: function expressions [function expression] (rather than functions declaration [function declarations, the difference refer to chapter 2nd of this series]) are not included in the Vo[variable object].
The Variable object (Variable object) is an abstract concept in a different context, which represents the use of a different object. For example, in the global context, the variable object is also the global object itself [Global object]. (This is where we can point to global variables through the properties of global objects).
Let's take a look at the global execution context in the following example:
var foo = 10;
function bar () {}////Functions Declaration
(function Baz () {}); function expression
Console.log (
This.foo = = Foo,//True
Window.bar = = Bar//True
);
Console.log (BAZ); Reference error, Baz not defined
The variable object (VO) in the global context has the following properties:
Figure 7. Global variable Object
As shown above, the function "Baz" is not included in the variable object as a function expression. This is why an attempt to access a reference error (REFERENCEERROR) occurs outside the function. Note that ECMAScript is only capable of creating new scopes when compared to other languages (such as C + +). Variables defined inside the function and intrinsic functions, are not directly visible externally and do not pollute the global object. When using eval, we will also use a new execution context (eval creation). Eval uses the global variable object or the caller's variable object (the source of the call to eval).
What about the function and its own variable object? In the context of a function, a variable object is represented as an active object (Activation object).
Active objects (Activation object)
When the function is activated by the caller, the special active object (activation object) is created. It contains common parameters (formal parameters) and special parameter (arguments) objects (parameter mapping tables with indexed properties). The active object is used as a variable object in the context of the function.
That is: The variable object of the function remains the same, except that the storage variable and the function declaration are included, as well as the special object arguments.
Consider the following scenario:
function foo (x, y) {
var z = 30;
function bar () {}//Functions Declaration
(function Baz () {}); function expression
}
Foo (10, 20);
The next Activation object (AO) of the "Foo" function context is as follows:
Figure 8. Activating an Object
In the same sense, function expression is not in the ranks of the AO.
The details of this AO can be found in chapter 9th of this tutorial series.
What we're going to talk about is the third main object. As we all know, in ECMAScript, we use intrinsic functions [inner functions], in which we may refer to its parent function variables, or global variables. We make these variable objects context scoped objects [scope object of the context]. Similar to the prototype chain discussed above [prototype chain], here we call the scope chain [scope chain].
Scope chain (Scope Chains)
A scope chain is a list of objects this is searched for identifiers appear in the code of the context.
The scope chain is an object list (list of objects) that retrieves the identifier (identifiers) that appears in the context code.
The principle of the scope chain is similar to the prototype chain, if the variable is not in its own scope, then it looks for the parent until the topmost level.
identifiers [Identifiers] can be understood as variable names, function declarations, and common parameters. For example, when a function needs to refer to a variable within its own function, but the variable is not declared within the function (or is not a parameter name), then the variable can be called a free variable [variable]. Then we need to use the scope chain to search for these free variables.
In general, a scope chain includes the parent variable object (variable object) (the top of the scope chain), the function itself variable VO, and the active object (Activation object). In some cases, however, other objects are included, such as dynamically joining a scope chain-for example with or catch statements-during execution. [Note: With-objects refers to a temporary scope object resulting from the With statement; Catch-clauses refers to a catch clause, such as catch (e), which produces an exception object, resulting in a scope change].
When the identifier is found, it is searched from the active object portion of the scope chain, and then (if the identifier is not found in the active object) finds the top of the scope chain, repeating itself, as if it were a scope chain.
var x = 10;
(function foo () {
var y = 20;
(function bar () {
var z = 30;
"X" and "Y" are free variables
is found in the next object in the scope chain (after the interaction object of the function "bar")
Console.log (x + y + z);
})();
})();
We assume that the object linkage of the scope chain is through a property called __parent__, which is the next object that points to the scope chain. This can be tested in rhino code, and this technique is actually implemented in the ES5 environment (there is a link called outer). Of course, you can also use a simple data to simulate this model. Using the __parent__ concept, we can present the above code in the following case. (therefore, the parent variable is in the [Scope] property of the function being present).
Figure 9. Scope chain
The scope chain is changed by using the WITH or catch statement during code execution. These objects are simple objects, and they also have a prototype chain. In this case, the scope chain is searched from two dimensions.
- First in the original scope chain
- The chain of the scope of each chain contact (if the link is prototype)
Let's look at the following example:
object.prototype.x = 10;
var w = 20;
var y = 30;
In the SpiderMonkey global object
For example, a variable object in the global context is inherited from the "Object.prototype"
So we can get "global variables without declarations."
Because you can get it 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 OH
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 removed from the scope chain
X can also be obtained from the context of Foo, noting that this value is back to 100.
"W" is also a local variable
Console.log (x, W); 100, 40
In the browser.
We can get the global W value by the following statement
Console.log (WINDOW.W); 20
})();
We will have the following structure diagram. This means that before we go to search for __parent__, we will first go to the __proto__ link.
Figure 10. With increased scope chain
Note that not all global objects are inherited by Object.prototype. The above diagram can be tested in SpiderMonkey.
Referencing external data from an intrinsic function is nothing special, as long as the variable objects of all the external functions are present--we simply traverse the scope list to find the desired variable. However, as mentioned above, when a context terminates, its state and itself will be destroyed (destroyed), and the intrinsic function will be returned from the external function. In addition, the returned function may be activated in other contexts, so what if a previously terminated context with some free variables is activated? Generally speaking, the concept of solving this problem is directly related to the scope chain in ECMAScript, which is called (lexical) closure ( Lexical) closure).
Closures (Closures)
In ECMAScript, the function is the "First Class" object. This noun means that the function can be passed as a parameter to other functions (in this case, the function is called "Funargs"-the abbreviation for "functional arguments"). A function that receives "Funargs" is called a higher-order function (Higher-order functions), or is more akin to a mathematical concept, called an operator (operators). The runtime of other functions also returns functions, which are called function valued functions (functions with functional value).
There are two conceptual problems with "Funargs" and "functional values", and these two sub-problems are called "Funarg problem" ("functional Parametric Problems"). To solve the problem of functional parameters accurately, it is necessary to introduce the concept of closure (closures). Let's take a closer look at these two issues (we can see that the [[Scope]] property of the function is used in ECMAScript to solve the problem).
"Funarg problem" a sub-problem is "upward funarg problem" [translation: May be translated as: The problem of function parameters looked up]. This problem occurs when a function returns to the outside from other functions. To be able to enter a variable in the external context at the end of the external context, the intrinsic function needs to be stored in the scope of the parent element of the [Scope] property when it is created (at creation moment). Then when the function is activated, the scope chain of the context behaves as a combination of the activation object and the [[Scope]] property (in fact, it can be seen):
Scope chain = Activation object + [[Scope]]
Scope chain = active Object + [[Scope]]
Note that the main thing is that the function saves the external scope when it is created because the saved scope chain (saved scope chain) will be used for variable lookups in future function calls.
function foo () {
var x = 10;
return function bar () {
Console.log (x);
};
}
The return of "Foo" is also a function
And the returned function is free to use the internal variable x
var returnedfunction = foo ();
global variable "x"
var x = 20;
Support for returned function
Returnedfunction (); The result is 10, not 20.
This form of scope is called the static scope [static/lexical scope]. The x variable above is the one found in the function bar [[Scope]]. In theory, there will also be dynamic scope [dynamic scope], which means that the above x is interpreted as 20 instead of 10. However, Emcascript does not use dynamic scopes.
Another type of "Funarg problem" is the top-down ["downward funarg problem"]. In this case, the upper and lower of the parent will exist, but there will be ambiguity when judging the value of a variable. That is, which scope this variable should use. Is it scoped at the time of function creation, or is it scoped at execution time? To avoid this ambiguity, you can use closures, which means using static scopes.
Take a look at the following example:
global variable "x"
var x = 10;
global function
function foo () {
Console.log (x);
}
(function (Funarg) {
Local variable "x"
var x = 20;
It doesn't have to be ambiguous.
Because we use the global variable "x" stored in [[Scope]] of the "foo" function,
is not an "x" of the caller scope
Funarg (); 10, not 20
}) (foo); Pass Foo as a "Funarg"
From the above, we seem to be able to conclude that in the language, the use of static scopes is a mandatory requirement for closures. However, in some languages, a combination of dynamic and static scopes is provided that allows the developer to choose which scope. In ECMAScript, however, only static scopes are used. So ECMAScript fully supports the use of [[Scope]] properties. We can define closures as follows:
A closure is a combination of a code block (in ECMAScript, a function) and statically/lexically saved all parent SC Opes.
Thus, via these saved scopes a function may easily refer free variables.
Closures are a series of code blocks (functions in ECMAScript), and the scope of all parents is saved statically. These saved scopes are searched for free variables in the function.
Note that since each normal function is created with [[Scope]], in theory, all functions in ECMAScript are closures.
There is also an important point where several functions may contain the same parent scope (this is a very common case, such as having several internal or global functions). In this case, the variables that exist in [Scope] are shared. Changes to variables in a closure can also affect another closure.
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 represented by this graph:
Figure 11. shared [[Scope]]
When you create multiple functions in a loop, you throw a puzzle. If you use a loop variable (such as "K") in a function that you create, all functions use the same loop variable, causing some programmers to often get no expected value. Now it's clear why this is the problem--because all functions share the same [[Scope]], where the loop variable is the last complex assignment.
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 a number of techniques to solve this kind of problem. One technique is to provide an additional object in the scope chain, such as adding a function:
var data = [];
for (var k = 0; k < 3; k++) {
DATA[K] = (function (x) {
return function () {
alert (x);
};
}) (k); Pass K as a parameter in
}
The results are correct
Data[0] (); 0
DATA[1] (); 1
DATA[2] (); 2
In-depth research and concrete practice of closure theory can be found in the 16th chapter of this series of tutorials (Closures). If you want more information about the scope chain, you can refer to the scope chain (scope chain) in chapter 14th of this series of tutorials.
The next section discusses the concept of a--this pointer to the last property of an execution context.
This pointer
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 relevant to the context in which the execution is performed. Therefore, it can also be referred to as the context object [context objects] (the contexts in which the execution context is activated).
Any object can be the this value for the context. I would like to clarify again some of the descriptions associated with the execution context in ECMAScript, especially this misunderstanding. Typically, this is incorrectly described as a property of a variable object. Recently, for example, it was found in this book (although the chapter that mentions this in the book is not bad). Please keep in mind that:
A This value was a property of the execution context, but not a property of the variable object.
This is a property of the execution context, not the property of a Variable object
This is important because, unlike variables, this is a process that does not have a similar search variable. When you use this in your code, this value is fetched directly from the execution context, not from the scope chain. The value of this only depends on when the context is entered.
By the way, unlike ECMAScript, Python has a self argument that is similar to this, but can be changed during execution. In ECMAScript, it is not possible to assign a value to this, because, or that sentence, this is not a variable.
In global context, the value of this refers to the global object, which means that the this value is the variable itself.
var x = 10;
Console.log (
X,//10
This.x,//10
window.x//10
);
In the function context, this may be a different value depending on the function call each time. This is provided by each caller, and caller is by invoking the expression [call expression] Generated (that is, how the function is invoked). For example, in the following example, Foo is a callee that is activated in the global context. The following example shows how different caller cause this to be different.
The alert in the "foo" function has not changed.
But this is different every time the call is activated.
function foo () {
alert (this);
}
Caller activates the callee "foo",
and provide "This" to this callee
Foo (); Global objects
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) (); is also a global object
(False | | bar.baz) (); is also a global object
var otherfoo = Bar.baz;
Otherfoo (); or a Global object
If you want to think deeply about the change of this value in each function call (and more importantly how it changes), you can read the 10th chapter of this tutorial series. The above mentioned situation will be discussed in detail here Zhangne.
Summary (conclusion)
Here we have completed a brief overview. Although it may not be that short, these topics need an entire book to complete. We don't mention two important topics: functions (Functions) (and different types of functions, such as function declarations and function expressions) and ECMAScript's evaluation strategy ( Evaluation strategy). These two topics can be consulted separately in this series of Tutorials 15th chapter function (Functions) and 19th evaluation Strategy (Evaluation strategy).
If you have any comments, questions or additions, I am very welcome to discuss them in the article comments.
I wish you all the best to learn ECMAScript smoothly.
In-depth understanding of the JavaScript series: JavaScript core (must read the master)