ECMA-262

1.JavaScript. The core. [url]http://dmitrysoshnikov.com/ecmascript/javascript-the-core/[/url][Prototype] property. However, in figures we will use __<internal-property>__ underscore notation instead of the double brackets, particularly for the prototype object: __proto__ (which is a real, but non-standard, feature in some engines, e.g. SpiderMonkey)

Prototype objects are also just simple objects and may have their own prototypes. If a prototype has a non-null reference to its prototype, and so on, this is called the prototype chain.

A prototype chain is a finite chain of objects which is used to implement inheritance and shared properties.


Consider the case when we have two objects which differ only in some small part and all the other part is the same for both objects. Obviously, for a good designed system, we would like to reuse that similar functionality/code without repeating it in every single object. In class-based systems, this code reuse stylistics is called the class-based inheritance — you put similar functionality into the class A, and provide classes B and C which inherit from A and have their own small additional changes.

ECMAScript has no concept of a class. However, a code reuse stylistics does not differ much (though, in some aspects it’s even more flexible than class-based) and achieved via the prototype chain. This kind of inheritance is called a delegation based inheritance (or, closer to ECMAScript, a prototype based inheritance).

Similarly like in the example with classes A, B and C, in ECMAScript you create objects: a, b, and c. Thus, object a stores this common part of both b and c objects. And b and c store just their own additional properties or methods.

console.clear();
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
};

console.log(b.calculate(20));//50
console.log(c.calculate(20));//60
console.log(a.__proto__);//Object {}
console.log(a.__proto__.__proto__);//null
console.log(a.__proto__.constructor);//Object()
console.log(Object.prototype);//Object {}
console.log(b.__proto__);//Object { x=10, calculate=function()}

We see that b and c have access to the calculate method which is defined in a object. And this is achieved exactly via this prototype chain.

The rule is simple: if a property or a method is not found in the object itself (i.e. the object has no such an own property), then there is an attempt to find this property/method in the prototype chain. If the property is not found in the prototype, then a prototype of the prototype is considered, and so on, i.e. the whole prototype chain (absolutely the same is made in class-based inheritance, when resolving an inherited method — there we go through the class chain). The first found property/method with the same name is used. Thus, a found property is called inherited property. If the property is not found after the whole prototype chain lookup, then undefined value is returned.

Notice, that this value in using an inherited method is set to the original object, but not to the (prototype) object in which the method is found. I.e. in the example above this.y is taken from b and c, but not from a. However, this.x is taken from a, and again via the prototype chain mechanism.

If a prototype is not specified for an object explicitly, then the default value for __proto__ is taken — Object.prototype. Object Object.prototype itself also has a __proto__, which is the final link of a chain and is set to null.


Often it is needed to have objects with the same or similar state structure (i.e. the same set of properties), and with different state values. In this case we may use a constructor function which produces objects by specified pattern.

3.Constructor
Besides creation of objects by specified pattern, a constructor function does another useful thing — it automatically sets a prototype object for newly created objects. This prototype object is stored in the ConstructorFunction.prototype property.

console.clear();
function Foo(y){
this.y = y;
}
//"Foo.prototype" stores reference to the prototype of newly created objects,
// so we may use it to define shared/inherited properties or methods

Foo.prototype.x = 10;
Foo.prototype.calculate = function(z){
return this.x + this.y + z;
};

var b = new Foo(20);
var c = new Foo(30);

console.log(b.calculate(30));//60
console.log(c.calculate(40));//80
console.log(b.__proto__);//Foo { x=10, calculate=function()}
console.log(Foo.prototype);//Foo { x=10, calculate=function()}
console.log(b.constructor);//Foo(y)

console.log(Foo.prototype.__proto__);//Object {}
//"Foo.prototype" automatically creates a special property "constructor", which is a reference to the constructor function itself


console.log(Foo.prototype.constructor);//Foo(y)

console.log(
b.__proto__ === Foo.prototype,//true
c.__proto__ === Foo.prototype,//true
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
);

This figure again shows that every object has a prototype. Constructor function Foo also has its own __proto__ which is Function.prototype, and which in turn also references via its __proto__ property again to the Object.prototype. Thus, repeat, Foo.prototype is just an explicit property of Foo which refers to the prototype of b and c objects.


Now, when we know basic object aspects, let’s see on how the runtime program execution is implemented in ECMAScript. This is what is called an execution context stack, every element of which is abstractly may be represented as also an object. Yes, ECMAScript almost everywhere operates with concept of an object

4.Execution context stack
There are three types of ECMAScript code: global code, function code and eval code. Every code is evaluated in its execution context. There is only one global context and may be many instances of function and eval execution contexts. Every call of a function, enters the function execution context and evaluates the function code type. Every call of eval function, enters the eval execution context and evaluates its code.

Notice, that one function may generate infinite set of contexts, because every call to a function (even if the function calls itself recursively) produces a new context with a new context state:


function foo(bar) {}

// call the same function,
// generate three different
// contexts in each call, with
// different context state (e.g. value
// of the "bar" argument)

foo(10);
foo(20);
foo(30);


An execution context may activate another context, e.g. a function calls another function (or the global context calls a global function), and so on. Logically, this is implemented as a stack, which is called the execution context stack.

A context which activates another context is called a caller. A context is being activated is called a callee. A callee at the same time may be a caller of some other callee (e.g. a function called from the global context, calls then some inner function).

When a caller activates (calls) a callee, the caller suspends its execution and passes the control flow to the callee. The callee is pushed onto the the stack and is becoming a running (active) execution context. After the callee’s context ends, it returns control to the caller, and the evaluation of the caller’s context proceeds (it may activate then other contexts) till the its end, and so on. A callee may simply return or exit with an exception. A thrown but not caught exception may exit (pop from the stack) one or more contexts.

all the ECMAScript program runtime is presented as the execution context (EC) stack, where top of this stack is an active context.

When program begins it enters the global execution context, which is the bottom and the first element of the stack. Then the global code provides some initialization, creates needed objects and functions. During the execution of the global context, its code may activate some other (already created) function, which will enter their execution contexts, pushing new elements onto the stack, and so on. After the initialization is done, the runtime system is waiting for some event (e.g. user’s mouse click) which will activate some function and which will enter a new execution context.


As we said, every execution context in the stack may be presented as an object. Let’s see on its structure and what kind of state (which properties) a context is needed to execute its code.

5.Execution context
An execution context abstractly may be represented as a simple object. Every execution context has set of properties (which we may call a context’s state) necessary to track the execution progress of its associated code. In the next figure a structure of a context is shown:
|-------------------------------------------------------------------|
| Execution context |
|-------------------------------------------------------------------|
|Variable object | {vars, function declarations, arguments...} |
|Scope chain | [Variable object + all parent scopes] |
|thisValue | [Context object] |
|-------------------------------------------------------------------|

Besides these three needed properties (a variable object, a this value and a scope chain), an execution context may have any additional state depending on implementation.


Let’s consider these important properties of a context in detail.
6.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.

Notice, that function expressions (in contrast with function declarations) are not included into the variable object.

A variable object is an abstract concept. In different context types, physically, it’s presented using different object. For example, in the global context the variable object is the global object itself (that’s why we have an ability to refer global variables via property names of the global object).
Let’s consider the following example in the global execution context:
var foo = 10;
function bar(){} //function declaration, FD
(function baz(){}); //function expression, FE
console.log(this.foo == foo);//true
console.log(window.bar == bar);//true
console.log(this);//window
console.log(baz);//ReferenceError: baz is not defined

Then the global context’s variable object (VO) will have the following properties:
|---------------------------------------|
| Global VO |
|---------------------------------------|
| x | 10 |
| bar | <function> |
|---------------------------------------|


Notice, in ECMAScript only functions create a new scope. Variables and inner functions defined within a scope of a function are not visible directly outside and do not pollute the global variable object.
注意,在ECMAScript中,只有函数能够创建新的作用域。在函数作用域内定义的变量和内部函数在外部非直接可见,且不会污染全局变量对象。

Using eval we also enter a new (eval’s) execution context. However, eval uses either global’s variable object, or a variable object of the caller (e.g. a function from which eval is called 来自于eval的函数调用).


And what about functions and their variable objects? In a function context, a variable object is presented as an activation object.

7.Activation object

When a function is activated (called) by the caller, a special object, called an activation object is created. It’s filled with formal parameters and the special arguments object (which is a map of formal parameters but with index-properties). The activation object then is used as a variable object of the function context.

I.e. a function’s variable object is the same simple variable object, but besides variables and function declarations, it also stores formal parameters and arguments object and called the activation object.

Considering the following example:

function foo(x, y) {
var z = 30;
function bar() {} // FD
(function baz() {}); // FE
}

foo(10, 20);
we have the next activation object (AO) of the foo function context:
|---------------------------------------|
| Activation Object |
|---------------------------------------|
| x | 10 |
| y | 20 |
| arguments | {0: 10, 1: 20} |
| z | 30 |
| baz | <function> |
|---------------------------------------|
And again the function expression baz is not included into the variable/activate object.


And we are moving forward to the next section. As is known, in ECMAScript we may use inner functions and in these inner functions we may refer to variables of parent functions or variables of the global context. As we named a variable object as a scope object of the context, similarly to the discussed above prototype chain, there is so-called a scope chain.

8.Scope chain

A scope chain is a list of objects that are searched for identifiers appear in the code of the context.
作用域链是一个 对象列表(list of objects) ,用以检索上下文代码中出现的 标识符(identifiers) 。

The rule is again simple and similar to a prototype chain: if a variable is not found in the own scope (in the own variable/activation object), its lookup proceeds in the parent’s variable object, and so on.

Regarding contexts, identifiers are: names of variables, function declarations, formal parameters, etc. When a function refers in its code the identifier which is not a local variable (or a local function or a formal parameter), such variable is called a free variable. And to search these free variables exactly a scope chain is used.

In general case, a scope chain is a list of all those parent variable objects, plus (in the front of scope chain) the function’s own variable/activation object. However, the scope chain may contain also any other object, e.g. objects dynamically added to the scope chain during the execution of the context — such as with-objects or special objects of catch-clauses.

When resolving (looking up) an identifier, the scope chain is searched starting from the activation object, and then (if the identifier isn’t found in the own activation object) up to the top of the scope chain — repeat, the same just like with a prototype chain.

var x = 10;

(function foo() {
var y = 20;
(function bar() {
var z = 30;
// "x" and "y" are "free variables"
// and are found in the next (after
// bar's activation object) object
// of the bar's scope chain
console.log(x + y + z);
})();
})();

We may assume the linkage of the scope chain objects via the implicit __parent__ property, which refers to the next object in the chain.
parent variable objects are saved in the [[Scope]] property of a function.


[img]http://dl2.iteye.com/upload/attachment/0089/0521/a6de2d41-aaf1-3378-89c7-d08edd0839e8.png[/img]

At code execution, a scope chain may be augmented using with statement and catch clause objects. And since these objects are simple objects, they may have prototypes (and prototype chains). This fact leads to that scope chain lookup is two-dimensional: (1) first a scope chain link is considered, and then (2) on every scope chain’s link — into the depth of the link’s prototype chain (if the link of course 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
// context inherits from "Object.prototype",
// so we may refer "not defined global
// variable x", which is found in
// the prototype chain

console.log(x); // 10

(function foo() {

// "foo" local variables
var w = 40;
var x = 100;

// "x" is found in the
// "Object.prototype", because
// {z: 50} inherits from it

with ({z: 50}) {
console.log(w, x, y , z); // 40, 10, 30, 50
}

// 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); // 100, 40

// and that's how we may refer
// shadowed global "w" variable in
// the browser host environment
console.log(window.w); // 20

})();

we have the following structure (that is, before we go to the __parent__ link, first __proto__ chain is considered):

[img]http://dl2.iteye.com/upload/attachment/0089/0523/53e417da-9f72-3fc2-b45a-ada2c1ccc5dd.png[/img]

Notice, that not in all implementations the global object inherits from the Object.prototype. The behavior described on the figure (with referencing “non-defined” variable x from the global context) may be tested e.g. in SpiderMonkey.


Until all parent variable objects exist, there is nothing special in getting parent data from the inner function — we just traverse through the scope chain resolving (searching) needed variable. However, as we mentioned above, after a context ends, all its state and it itself are destroyed. At the same time an inner function may be returned from the parent function. Moreover, this returned function may be later activated from another context. What will be with such an activation if a context of some free variable is already “gone”? In the general theory, a concept which helps to solve this issue is called a (lexical) closure, which in ECMAScript is directly related with a scope chain concept.

9.Closures
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值