The core of JavaScript

Source: Internet
Author: User
Tags closure glob

Excerpt from: http://ourjs.com/detail/529bc7e44c742ef031000002

Object

ECMAScript is a highly abstract object-oriented language that interacts with objects . Even though there are basic types inside the ECMAScript, they are also converted to objects when needed.

An object is a collection of properties and has a separate prototype (prototype) object. This prototype can be an object or null.

Let's look at a basic example of an object. The Prototype of an object is referenced by an internal [[Prototype]] property. However, in the inside we will use ____ the underscore mark instead of the two brackets, for the prototype object is: __proto__ .

For the following code:

var foo = {  x:10,  y:20};

We have a structure with two distinct properties and an implied __proto__ property, which is a reference to the foo prototype object:

What's the use of these prototype? Let's use the concept of the prototype chain (prototype chain) to answer this question.

Prototype chain

Prototype objects are also simple objects and can have their own prototypes. If the prototype of a prototype object is a non-null reference, then so on, this is called the prototype chain .

A prototype chain is a finite object chain used to implement inheritance and shared properties.

Considering a situation like this, we have two objects, only a small part of them, and the rest are the same. Obviously, for a well-designed system, we will reuse similar functionality/code rather than repeating it in each individual object. In a class-based system, this code reuse style is known as class inheritance -you put similar functionality into a class A , and B classes and classes C inherit classes and A have some minor extra changes of their own.

There is no concept of class in ECMAScript. However, the style of code reuse is not much different (although in some ways more flexible than a class-based (class-based) approach) and is implemented through a prototype chain . This type of inheritance is known as Delegate inheritance (delegation based inheritance) (or, closer to ECMAScript, called Prototype inheritance (prototype based inheritance ))。

As with the class in the example, A B C similar to the one in ECMAScript you created the object: a ,, b c . Thus, objects a are stored in objects b and c in common parts. Then b and c only store their own extra 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 methodb.calculate (+);//60c.calculate (40);//80

Simple enough, isn't it? We see b and c access the methods defined in the object a calculate . This is achieved through the prototype chain.

The rule is simple: if a property or a method cannot be found in the object itself (that is, the object does not have a property of its own), then it attempts to find the property/method in the prototype chain. If this attribute is not found in the prototype, then the prototype will be looked up, and so on, traversing the entire prototype chain (which, of course, is the same in class inheritance, when parsing an inherited method -We traverse the class chain (class Chain)). The first property/method found with the same name will be used. Therefore, a property that is found is called an inheritance property. If this property is not found after traversing the entire prototype chain, the value is returned undefined .

Note that the value used in the Inheritance method this is set to the original object, not the (prototype) object in which the method is found. That is, in the above example, the value in this.y and is taken b c , not the a value in. However, it is the value in which it is this.x taken a , and it is done again through the prototype chain mechanism.

If you do not explicitly specify a prototype for an object, then it will use __proto__ the default value- Object.prototype . The Object.prototype object itself also has a __proto__ property, which is the end of the prototype chain and the value is null .

The next diagram shows the a b c inheritance hierarchy between objects, and:

Note: ES5 standardizes an optional method for implementing prototype inheritance, which is to use Object.create functions:

var B = Object.create (A, {y: {value:20}}), var c = Object.create (A, {y: {value:30}});

You can get more information about the ES5 new API in the corresponding section. ES6 standardizes __proto__ properties and can be used when an object is initialized.

Typically, an object is required to have the same or similar state structure (that is, the same set of attributes), with different state values assigned. In this case we may need to use the constructor (constructor function), which creates the object in the specified pattern .

constructor function

In addition to creating objects in the specified schema, the constructor does another useful thing-it automatically sets a prototype object for the newly created object . This prototype object is stored in the ConstructorFunction.prototype attribute.

In other words, we can use constructors to override the previous example of owning object b and Object C . Therefore, the role of object a (a prototype object) is played by Foo.prototype :

A constructor functionfunction Foo (y) {//which may create objects//by specified Pattern:they has after//CREA tion Own "Y" property This.y = y;} Also "Foo.prototype" stores reference//to the prototype for newly created objects,//so we could use it to define shared/  inherited//properties or methods, so the same as in//previous example we have://inherited property "x" foo.prototype.x = 10;//and inherited Method "Calculate" Foo.prototype.calculate = function (z) {return this.x + This.y + z;};/ /Now create our "B" and "C"//objects using "pattern" Foovar B = new Foo (+); var c = new Foo (+);//Call the inherited me Thodb.calculate (30); 60c.calculate (40); 80//Let's show that we reference//properties we expectconsole.log (b.__proto__ = = = Foo.prototype,//True C.__proto __ = = Foo.prototype,//TRUE//also "Foo.prototype" automatically creates//a special property "constructor", which I  s A//reference to the constructor function itself; Instances "B" and "C" May found it via//delegation and use to check their constructor b.constructor = = Foo,//true c.constructor = = = Foo ,//true Foo.prototype.constructor = = = Foo//true b.calculate = = = B.__proto__.calculate,//True B.__proto__.calculat E = = = Foo.prototype.calculate//TRUE);

This code can be expressed as the following relationship:

This diagram illustrates once again that each object has a prototype. The constructor Foo also has its own __proto__ value Function.prototype , which is Function.prototype also associated through its __proto__ properties Object.prototype . So, to reiterate, Foo.prototype is Foo a definite attribute that points to b c the prototype of objects and objects.

Formally, if you think about the concept of classification (and we've Foo sorted it out), then the constructors and prototype objects together can be called "classes". In fact, for example, Python's first-level (first-class) dynamic class (classes) is obviously implemented with the same 属性/方法 processing scheme. From this point of view, the class in Python is a syntactic sugar inherited by the delegate used by ECMAScript.

Note: The concept of "class" is standardized in ES6, and is actually implemented as a syntactic sugar built on top of the constructor, as described above. From this point of view the prototype chain becomes a concrete implementation of class inheritance:

Es6class Foo {  Constructor (name) {    this._name = name;  }  GetName () {    return this._name;  }} Class Bar extends Foo {  getName () {    return super.getname () + ' Doe ';  }} var bar = new Bar (' John '); Console.log (Bar.getname ()); John Doe

A complete and detailed explanation of this topic can be found in the seventh chapter of the ES3 series. It is divided into two parts: 7.1 Object-oriented. Basic theory, where you'll find a comparison of various object-oriented paradigms, style descriptions, and their comparisons with ECMAScript, and then object-oriented in 7.2. ECMAScript implementation is an object-oriented introduction to ECMAScript.

Now, after we know the basics of the object, let's look at how the run-time program executes in ECMAScript execution. This is called the execution context stack (execution context stack), where each element can also be abstracted as an object. Yes, ECMAScript almost anywhere with the concept of objects;)

Execution context Stack

There are three types of ECMAScript code: Global Code, function code, and eval code. Each code is evaluated in its execution context (execution context). There is only one global context, and there may be multiple function execution contexts and eval execution contexts. Each invocation of a function enters into the context of the function execution and evaluates the function code type. Each eval call to a function enters the eval execution context and evaluates its code.

Note that a function may create countless contexts, because every call to a function (even if the function recursively calls itself) generates a context with a new state:

function foo (bar) {}//Call the same function,//generate three different//contexts in each call, with//different Contex T state (e.g value//of the "bar" argument) foo (ten); foo (30);

An execution context may trigger another context, such as a function calling another function (or calling a global function in the global context), and so on. Logically, this is implemented in the form of a stack, which is called the execution context stack .

A context that triggers another context is called caller. The context that is triggered is called callee. Callee at the same time may be some other callee caller (for example, a function that is called in the global context, followed by some intrinsic functions).

When a caller triggers (invokes) a callee, the caller will suspend its execution and pass control to callee. This callee is pushed to the stack and becomes a running (active) execution context. At the end of the callee context, it returns control to caller, and then the caller context continues to execute (it may trigger other contexts) until it ends, and so on. Callee may simply return or exit due to an exception . An exception that is thrown but not caught may exit (pop from the stack) one or more contexts.

In other words, the runtime of all ECMAScript programs can be represented by the execution context (EC) stack , which is currently active (Active) Context:

When the program starts it goes into the global execution context , which is at the bottom of the stack and is the first element in the stack. The global code then does some initialization to create the objects and functions that are needed. During the execution of the global context, its code might trigger other (already created) functions that will go into their own execution context, push new elements into the stack, and so on. When initialization is complete, the runtime system waits for events (for example, a user mouse click), and these events will trigger some functions to enter the new execution context.

In the next illustration, there are some function contexts EC1 and a global context, and the Global EC EC1 following stack will change when entering and exiting the global context:

This is how the ECMAScript runtime system really manages code execution.

More information about the execution context in ECMAScript can be obtained in the context of the corresponding first chapter execution.

As we said, each execution context in the stack can be represented by an object. Let's take a look at its structure and what state (what property) a context needs to execute its code.

Execution context

An execution context can be abstracted to represent a simple object. Each execution context has properties (which can be called context State ) that are used to track the execution of code associated with it. The structure of a context is shown in:

In addition to these three required attributes (a variable object (variable objec), a this value, and a scope chain (scope chain)), The execution context can have any additional state, depending on the implementation.

Let's take a closer look at these important attributes in the context.

Variable Object
Variable objects are data scopes that are related to the execution context. It is a special context-sensitive object that stores the variables and function declarations defined in the context.

Note that the function expression (as opposed to the function declaration ) is not included in the variable object.

A variable object is an abstract concept. For different context types, physically, a different object is used. For example, a variable object in the global context is the global object itself (which is why we can associate global variables with the global object's property name).

Let's consider the following example in the context of global execution:

var foo = 10;function bar () {}/function declaration, FD (function Baz () {}); function expression, FEconsole.log (  this.foo = = Foo,//true  Window.bar = = bar//True); Console.log (baz);//Re Ferenceerror, "Baz" is not defined

Then, the variable object of the global context (variable objec, or VO) will have the following properties:

Again, the function baz is a function expression that is not included in the variable object. That's why it occurs when we want to access it outside of the function itself ReferenceError .

Note that only functions in ECMAScript can create a new scope compared to other languages, such as C + +. Variables and intrinsic functions defined in the scope of a function are not directly accessible outside the function and do not pollute the global variable object.

Using eval We will also enter a new execution context (eval type). In any case, eval use a global variable object or a variable object that uses caller (such as eval the function that is being called).

So what about the function and its variable object? In the context of a function, a variable object is represented by an active object (Activation objects).

Active Object

When a function is triggered by caller (called), a special object, called the active object (Activation objects), is created. This object contains the formal parameters and that particular arguments object (which is a mapping of the parameters, but the values are obtained through the index). The active object is then used as a variable object for the function context.

In other words, the variable object of a function is also an equally simple variable object, but in addition to variables and function declarations, it also stores formal parameters and arguments objects, and is called the active object .

Consider the following example:

function foo (x, y) {  var z = +;  function bar () {}//FD  (function Baz () {});//Fe}foo (10, 20);

Let's look at the foo active object in the context of the function (activation object, abbreviation AO):

and the function expression baz is not included in the variable/active object.

A complete description of all the details of this topic (hoisting) , such as variable and function declarations, can be found in chapter II variable objects of the same name.

Note that in ES5, variable objects and active objects are incorporated into the lexical environment model (lexical environments models), and detailed descriptions can be found in the corresponding chapters.

Then we move on to the next section. It is well known that in ECMAScript we can use intrinsic functions , and then in these intrinsics we can refer to the variables of the parent function or the variables in the global context. When we name the variable object the scope object of the context, similar to the prototype chain discussed above, there is something called a scope chain .

Scope chain
作用域链是一个对象列表,上下文代码中出现的标识符在这个列表中进行查找。

This rule is also as simple and similar as the prototype chain: If a variable is not found in the scope of the function itself (in its own variable/active object), then it will look for the variable object of its parent function (the outer function), and so on.

In terms of context, identifiers refer to: variable names , function declarations, formal parameters, and so on. When a function refers to an identifier that is not a local variable (or a local function or a formal parameter) in its code, the identifier is called a free variable . search for these free variables (free variables) is just about to use the scope chain .

In general, a scope chain is a list of all parent (function) variable objects __ Plus (in the scope chain header) function itself variable/activity object . However, this scope chain can also contain any other objects, such as objects that are dynamically added to the scope chain during context execution-like with objects or special catch clause (catch-clauses) objects.

When an identifier is parsed (found), it is looked up from the active object in the scope chain, and then (if the identifier is not found in the active object of the function itself) to the previous level of the scope chain-repeating the process, just like the prototype chain.

var x = ten; (function foo () {  var y = +)  ; (function bar () {    var z =;    "X" and "Y" is "free variables"    //and is found in the next (after    /bar ' s Activation object) object    // of the bar ' s scope chain    console.log (x + y + z);  }) ();}) ();

We can assume that an implicit property is associated with a __parent__ scope chain object that points to the next object in the scope chain. This scenario may have been tested in real rhino code, and the technique is clearly used in the lexical context of ES5 (where it is called a outer connection). Another representation of a scope chain can be a simple array. Using the __parent__ concept, we can use the following diagram to represent the above example (and the parent variable object is stored in the function's [[Scope]] properties):

Scope chains can be enhanced by using with statements and subordinate clauses during code execution catch . And since these objects are simple objects, they can have prototypes (and prototype chains). This fact causes the scope chain lookup to become two dimensions : (1) First is the scope chain connection, then (2) on each scope chain connected-the prototype chain that goes deep into the scope chain (if this connection has a prototype).

For this example:

object.prototype.x = 10;var W = 20;var y = 30;//in SpiderMonkey global object//i.e. variable object of the global//cont Ext inherits from "Object.prototype",//So we may refer ' not defined global//variable x ', which is found in//the Prototy PE chainconsole.log (x); Ten (function foo () {  //"foo" local variables  var w = +;  var x = +;  "X" is found in the  //' Object.prototype ', because  //{z:50} inherits from it with  ({z:50}) {    console . log (w, x, Y, z); +, +, (+)  //After ' with ' object  is removed//from the scope chain, ' x ' is  //again found in The AO of "foo" context;  Variable "W" is also local  console.log (x, W);//, +//And that's how  we may refer  //shadowed Glob Al "W" variable in  //The browser host environment  Console.log (WINDOW.W);//20});

We can give the following structure (to be exact, __parent__ find the chain first before we find the connection __proto__ ):

Note that not all implementations in the global object are inherited from Object.prototype . The behavior described in (referencing "undefined" variables from the global context x ) can be tested in such a spidermonkey engine.

Because all the parent variable objects are present, there is nothing special in getting the data in the parent function in the inner function-we just traverse the scope chain to parse (search) the required variables. As we mentioned above, after the end of a context, all its state and itself will be destroyed . At the same time, the parent function may return an intrinsic function . Furthermore, the returned function may be called later in another context. If the context of a free variable has been "gone", what will happen to such a call? Generally speaking, there is a concept that can help us solve this problem, called (lexical) closure , which is closely related to the concept of the scope chain in ECMAScript.

Closed Package

In ECMAScript, the function is the first level (first-class) object. The term means that a function can be passed as a parameter to other functions (in that case, these arguments are called "function type arguments" (Funargs, which is the abbreviation for "functional arguments"). Functions that receive "function type parameters" are called higher-order functions or, close to the mathematical ones, as higher-order operators . The same function can also be returned from other functions. Functions that return other functions are known as functions that function as values (function valued) (or functions that have function class values (functions with functional value)).

There are two issues that are conceptually related to the function type parameter (Funargs) and the function type value (functional values). And these two sub-problems are common in "Funarg problem"(or "functional argument" problem). In order to solve the whole "funarg problem", the concept of closure (closure) was created. We describe these two sub-problems in detail (we will see that both of these problems are solved using the properties of the functions mentioned in the diagram in ECMAScript [[Scope]] ).

The first sub-issue of the "funarg problem is the " up Funarg problem "(upward funarg problem). It appears when a function returns from another function (to the outer layer) and uses the free variable mentioned above. In order to access the variables within the context of the parent function , the inner function saves the scope chain of the parent function in its properties when it is created . [[Scope]] So when the function is called , the scope chain of its context is formatted as the Activity object and the [[Scope]] attribute's and (actually what we just saw in it):

Scope chain = Activation object + [[Scope]]

Again note this key point--exactly at the time of Creation --the function will save the scope chain of the parent function , because the exact scope chain will be used to look for variables in future function calls.

function foo () {  var x = ten;  return function bar () {    console.log (x);  };} "Foo" returns also a function//and this returned function uses//free variable "x" var returnedfunction = foo ();//Glob Al variable "x" var x = 20;//Execution of the Returned functionreturnedfunction (); Ten, but not 20

This type of scope is called a static (or lexical) scope. We see that the variable x is found in the properties of the returned bar function [[Scope]] . In general, there are also dynamic scopes, so the variables in the example above x will be parsed 20 instead 10 . However, dynamic scopes are not used in ECMAScript.

The second part of the "funarg question is" down Funarg problem ". There may be a parent context in this case, but it may be ambiguous when parsing an identifier. The question is: which scope value does the identifier use-stored statically at the time the function was created or dynamically generated during execution (such as the scope of the caller )? To avoid this ambiguous situation and to form closures, the static scope is used:

Global "X" var x = 10;//Global functionfunction foo () {  console.log (x);} (function (funarg) {  //local "x"  var x = +;  There is no ambiguity,  //Because we use global "X",  //which were statically saved in  //[[Scope]] of the " Foo "function,  //But not the" x "of the caller ' s scope,  //which activates the" Funarg "  funarg (); Not) (foo); Pass "Down" foo as a "Funarg"

We can conclude that a static scope is a required condition for a language to have closures. However, some languages may provide both dynamic and static scopes, allowing programmers to make choices-what should be included (closure) and what should not be included. Since only static scopes are used in ECMAScript (for example funarg问题 , we have solutions for the two sub-problems we have), the conclusion is thatECMAScript fully supports closures and is technically implemented through the properties of functions [[Scope]] . Now we can give an exact definition of the closure:

闭包是一个代码块(在ECMAScript是一个函数)和以静态方式/词法方式进行存储的所有父作用域的一个集合体。所以,通过这些存储的作用域,函数可以很容易的找到自由变量。

Note that since each (standard) function is saved at the time of Creation [[Scope]] , all functions in ECMAScript are theoretically closed .

Another important thing to note is that multiple functions may have the same parent scope (this is a very common case, such as when we have two internal/global functions). In this case, the [[Scope]] variables stored in the attribute are shared among all functions that have the same parent scope chain. The modification of a closure to a variable is reflected in the reading of these variables by 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 explained by:

This feature is very confusing when it comes to creating multiple functions in a loop. When using the loop counter in the created function, some programmers often get unintended results, and the counters in all functions are the same value. Now it's time to uncover the answer-because all of these functions have the same value [[Scope]] , the values of the loop counter in this property are the last assigned value.

var data = [];for (var k = 0; k < 3; k++) {  Data[k] = function () {    alert (k);  };} Data[0] (); 3, but not 0data[1] (); 3, but not 1data[2] (); 3, but not 2

Here are a few techniques to solve this problem. One of these is to provide an extra object in the scope chain-for example, using extra functions:

var data = [];for (var k = 0; k < 3; k++) {  data[k] = (function (x) {    return function () {      alert (x);    } ;  }) (k); Pass "K" value}//now it is correctdata[0] (); 0DATA[1] (); 1DATA[2] (); 2

Students who are interested in closure theory and their practical application can find additional information in the sixth chapter of the closure package. If you want to get more information about the scope chain, you can look at the fourth scope chain with the same name.

This
this是一个与执行上下文相关的特殊对象。因此,它可以叫作上下文对象(也就是用来指明执行上下文是在哪个上下文中被触发的对象)。

any object can be used as a value in the context this . I would like to clarify once again the misunderstanding in some of the descriptions of ECMAScript execution contexts and sections this . thisis often incorrectly described as a property of a variable object. Such errors exist in the book (even though the relevant chapters of the book are very good). Repeat once more:

this是执行上下文的一个属性,而不是变量对象的一个属性

This feature is important because, contrary to variables , the this identifier parsing process is never involved . In other words, when accessed in code, this its value is obtained directly from the execution context and does not require any scope chain lookups . thisThe value is determined only once when the context is entered .

By the way, as opposed to ECMAScript, for example, the Python method has a parameter that is treated as a simple variable, and the self value of the variable is the same in each method and can be changed to another value during execution. In ECMAScript, this it is not possible to assign a new value because, again, it is not a variable and does not exist in the variable object.

In the global context, this it is equal to the global object itself (which means that this is equal to the this variable object ):

var x = 10;console.log (  x,//  this.x,//  window.x//10);

In the case of a function context, each call to a function may have a this different value. This this value is provided by caller in the form of a function call expression (that is, how the function is called). For example, the following function foo is a calleecalled in the global context, which is caller. Let's take a look at the example, for a function with the same code, this how the value is in a different call (the different ways the function fires), the caller gives different results:

The code of the ' foo ' function//never changes, but the ' this ' value//differs in every activationfunction foo () {  alert (this);} Caller activates "foo" (callee) and//provides "This" for the Calleefoo (); Global Objectfoo.prototype.constructor (); Foo.prototypevar bar = {  Baz:foo};bar.baz ();//Bar (Bar.baz) ();//also bar (Bar.baz = Bar.baz) ();//But this is G Lobal object (Bar.baz, Bar.baz) (); Also global object (False | | bar.baz) (); Also global objectvar Otherfoo = Bar.baz;otherfoo (); Again global object

In order to understand this in depth why (and more fundamentally- how ) each function call may change, you can read chapter III, this. There, the above mentioned situation will be discussed in detail.

Summarize

Through this article we have completed a summary of the summary. Although, it does not look like a "summary";). Full interpretation of all these topics requires a complete book. We're just not involved in two big themes: functions (and differences between functions, such as function declarations and function expressions ) and evaluation strategies used in ECMAScript ( Evaluation strategy). These two themes can be found in the corresponding chapters of the ES3 series: the fifth chapter function and the eighth chapter evaluation strategy.

Then we move on to the next section and consider the last property of the execution context. This is this the concept of a value.

The core of JavaScript

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.