V8引擎实现标准ECMA-262(三)

推荐英文原址 ECMA-262

3.构造函数javascript

构造函数除了经过指定的模式建立对象之外,还有另一个好处——它可以自动设置新建立对象的原型对象,这个原型对象存储在构造函数的Prototype属性中。html

例如,咱们使用构造函数来建立对象b和c,以下java

[javascript]  view plain copy
  1. // 构造函数  
  2. function Foo(y) {  
  3.   // 经过指定模式建立对象,对象建立完后,就拥有了y属性  
  4.   this.y = y;  
  5. }  
  6.    
  7. //Foo.prototype存储了新建立对象的原型的引用,咱们可使用它来定义共享的属性或方法   
  8. //以下这句,使全部新建立的对象有用共享的属性 "x"  
  9. Foo.prototype.x = 10;  
  10.    
  11. // 全部新建立的对象有用共享的方法 "calculate"  
  12. Foo.prototype.calculate = function (z) {  
  13.   return this.x + this.y + z;  
  14. };  
  15.    
  16. // 使用Foo构造函数建立对象b和c,注意使用new调用构造函数建立对象  
  17. var b = new Foo(20);  
  18. var c = new Foo(30);  
  19.    
  20. // 调用继承的方法,该方法为两者共享的,只有一份拷贝  
  21. b.calculate(30); // 60  
  22. c.calculate(40); // 80  
  23.    
  24.   
  25. console.log(  
  26.  //对象b和c原型相同  
  27.   b.__proto__ === Foo.prototype, // true  
  28.   c.__proto__ === Foo.prototype, // true  
  29.    
  30.  // Foo.prototype 会自动建立一个特别的属性constructor指向构造函数Foo  
  31.  //对象b和c能够经过代理找到此构造函数  
  32.   b.constructor === Foo, // true  
  33.   c.constructor === Foo, // true  
  34.   Foo.prototype.constructor === Foo // true  
  35.    
  36.   b.calculate === b.__proto__.calculate, // true  
  37.   b.__proto__.calculate === Foo.prototype.calculate // true  
  38.    
  39. );  
以上对象的关系以下图所示,


此图看起来很是复杂,我会着重来分析的,容我翻译完本节回头再写^_^python

这张图展现了每个对象都有一个对应的原型。构造函数Foo自身也有原型,该原型由Foo的属性_ _proto_ _所指向,如图Function.prototype,而Function.prototype自己又有原型,即Object.prototype。所以,Foo.prototype只是Foo的显式属性,它被对象b和c引用。ecmascript

正常状况下,若是不考虑类的概念,咱们能够把构造函数和原型统称为“类”。事实上,python的动态类就是利用属性/方法手段来实现的。从这点上来看,python的类语法只是ECMAScript代理继承的一种浓缩(syntactic sugar一词翻译ide

这个主题的详细完整解释放在了ES3 系列第7章,共有两个部分:面向对象理论和ECMAScript实现。函数

如今,若是你有基本的面向对象的观点,咱们来看看ECMAScript中运行时程序执行的实现。咱们也叫作程序执行上下文栈,其中的每一个抽象元素都由对象表示,实际上,ECMAScript时刻都透露着面向对象的思想。ui


4.执行上下文栈this

ECMAScript有三种类型的代码:全局代码、函数代码和eval代码。每种代码在它的执行上下文中进行。全局上下文的实例只有一个,而函数和eval上下文却有多个实例。每次函数或者eval调用都会进入相应的函数或者eval的执行上下文中,计算函数或者eval的代码类型。(后面一句至关拗口,说白了就是每次函数调用前将一些状态压入栈中保存,从而在函数代码的执行时,处在当前的函数执行的上下文内。懂得基本的汇编函数调用对理解这个大有益处。)spa

请注意,每一个函数能够产生无数个上下文,由于每次函数被调用(即便函数被本身调用,即嵌套函数)都会产生一个新的上下文,并拥有当前上下文的状态。

[javascript]  view plain copy
  1. function foo(bar) {}  
  2.   
  3. //调用同一个函数三次,会产生三个的上下文,每一个上下文拥有不一样的状态,例如参数bar值不同(不少状况下不只仅与此)  
  4. foo(10);  
  5. foo(20);  
  6. foo(30);  
一个上下文激活另外一个上下文,那么前者就叫作调用者,后者叫做被调用者。被调用者可能同一时间又是调用者(例如一个函数被全局上下文调用,该函数又调用内部函数)。当调用者调用被调用者,那么调用者会暂停滋生的执行,并把控制流传给被调用者。被调用者被压入栈,变成一个运行(激活)的执行上下文,当被调用上下文结束,将会返回控制流给调用者,调用者再继续执行直到结束(期间可能又激活其余上下文)。被调用者返回return结果或者由于异常退出。一个非捕获异常可能致使从多个上下文退出(出栈)。

全部的ECMAScript程序运行时经过执行上下文(EC)栈来展示,栈最顶部的上下文是激活(正在运行着)的上下文,以下为一个执行上下文栈,

当程序开始时,先进入全局执行上下文(Global EC),它是栈中最底部也是第一个元素。全局代码进行一些初始化,建立必要的对象和函数 。在全局上下文执行的时候,它会激活其它上下文(函数调用),并将新元素压入栈。初始化之后,则等待一些事件来触发函数,从而进入新的上下文中。下图展示了函数上下文的变化状况,栈从上下文EC1进入和退出全局上下文的过程,

如咱们所说,每一个栈中的执行上下文都是一个对象,让咱们来看看它的结构以及上下文须要哪些状态来运行代码。

5.执行上下文

执行上下文抽来来讲就是一个简单对象。每个执行上下文都有一些属性的集合(上下文状态),用来跟踪相关代码的执行过程。下图即为上下文的结构图

[javascript]  view plain copy
  1. <span style="font-family: 'Microsoft YaHei'; white-space: normal; background-color: rgb(255, 255, 255); ">除了这三种必要属性外,执行上下文在实现上能够有其它的状态。咱们来看看这些重要属性的细节。</span>  

变量对象

变量对象是跟上下文有关的数据的范围,这个对象存储了在上下文中定义的变量、函数声明

注意,函数表达式(对比函数声明)不包括在变量对象之中

变量对象是一个抽象的概念,在不一样的上下文类型中,会使用不一样的对象。例如,在全局上下文中,变量对象就是全局对象自己(这就是为何咱们可以经过全局对象的属性名来引用全局变量),以下:

[javascript]  view plain copy
  1. var foo = 10;  
  2.    
  3. function bar() {} // 函数声明, FD  
  4. (function baz() {}); //函数表达式, FE  
  5.    
  6. console.log(  
  7.   this.foo == foo, // true  
  8.   window.bar == bar // true  
  9. );  
  10.    
  11. console.log(baz); // 引用错误, "baz" 未定义  
全局上下文变量对象(VO)会有下列属性

再一次看到,函数baz做为一个函数表达式没有包括在变量对象中。这就是为何在外部访问函数自身时产生引用错误。

注意,相对于其余语言(如C++),在ECMAScript只有函数会建立一个新的范围。变量和在函数内定义的内部函数在外部不可见,不会污染全局对象变量。

使用eval也会进入一个新的eval执行上下文,而后,eval使用全局变量对象或者调用eval的变量对象(例如调用eval的某个函数)

函数和其变量对象是什么样的?在函数上下文中,变量对象表现为一个激活对象(activation object)

激活对象

当一个函数被调用时,一个特别的激活对象就会被建立。形式参数和一个特别的argument对象(它是对形参的一种映射,能够经过索引寻找指定的形参)将会用来对它赋值,而后激活对象就能够做为一个函数的变量。例如,一个函数的变量对象不只存储了函数的变量和函数声明,它也存储了形式参数和argument对象,它就叫作激活对象。例以下面这个例子:

[javascript]  view plain copy
  1. function foo(x, y) {  
  2.   var z = 30;  
  3.   function bar() {} // FD  
  4.   (function baz() {}); // FE  
  5. }  
  6.    
  7. foo(10, 20);  
咱们有了函数上下文的另外一个对象。


                      

函数表达式baz仍然不包括在变量/激活对象中。

咱们继续进行下一节。如咱们所知,在ECMAScript中咱们使用内部函数,这些内部函数可能引用父函数或者全局函数的变量,就像上文提到的原型链,咱们给做用域对象起名为做用域链。

做用域链

咱们做用域链是一个对象列表,它用来寻找出如今上下文代码中的特定符号。

相关文章
相关标签/搜索