接着之前两个帖子发,现在简单介绍下javascript 函数使用分析:
  函数调用过程与作用域链 
讲到作用域链,就要扯到函数的调用。当我们有一个函数 
function fn(param) {} 
我们去调用它 
fn(1); 
这个时候解析器为我们做了什么呢? 
有一定经验的javascript工程师也许会用过arguments、用过闭包、知道作用域,这一切的一切,都和execution context有关。 
当我们进入一个函数调用的时候,解析器会为我们创建一个活动对象(Activation Object ),假设这里把这个活动对象叫做ac(为什么不叫ao呢,因为喜欢c)。然后做下面的事情: 
1. 初始化arguments对象,并将它添加到这个ac中。这个时候,对象ac就拥有了一个name为arguments的成员。这里arguments初始化过程就不具体说了,感兴趣的可以看ecma262的章节10.1.8。 
2. 解析形参,并使用函数调用时传递的参数初始化。在上面的调用例子fn(1)中,这个时候,ac就拥有了一个name为param的成员,这个成员的值为1。 
3. 对function declaration进行初始化,为所有FunctionBody中的function declaration,创建function Object,并添加到对象ac中作为ac的成员。在这一步,假设ac中已经包含了同名属性,会被覆盖掉。 
4. 对var声明进行初始化,为所有var声明,在对象ac中创建同名成员,并初始化为undefined。在这一步,假设ac中已经包含了同名属性,不会被覆盖掉。 
5. 初始化作用域链,并将这个作用域链与当前的执行上下文相关联。这个作用域链是一个链式列表,最前段是进入函数调用时初始化出来的活动对象ac,然后后面跟着的是该函数的[[scope]]的成员。[[scope]]是个什么东西呢,就是这个链。假如函数体中有创建function Object,叫做innerFn,那innerFn的[[scope]]成员,就是这个作用域链。当innerFn被调用时,会初始化新的活动对象,新的作用域链。新的作用域链就是初始化自这个新的活动对象和innerFn的[[scope]]。 
那scope chain是什么作用呢?看下面的描述,来自10.1.4 
During execution, the syntactic production PrimaryExpression : Identifier is evaluated using the following algorithm: 
1. Get the next object in the scope chain. If there isn't one, go to step 5. 
2. Call the [[HasProperty]] method of Result(1), passing the Identifier as the property name. 
3. If Result(2) is true, return a value of type Reference whose base object is Result(1) and whose property name is the Identifier. 
4. Go to step 1. 
5. Return a value of type Reference whose base object is null and whose property name is the Identifier. 可以看出,我们在访问一个变量的时候,其实是从和当前执行上下文相关的作用域链中查找成员。 
在程序正常在全局下的函数,其[[scope]]成员的值是global object,所以无论任何调用,在作用域链的尾端,一定会是global object。在浏览器宿主环境下,就是window。 
    函数调用过程中的this 
在函数的调用中,this是个什么东西,又是由什么决定的呢?在ecma262中,这是个比较绕的东西,其描述散落在世界各地。 
首先,在10.2.3中告诉我们: The caller provides the this value. If the this value provided by the caller is not an object (note that null is not an object), then the this value is the global object. 我们可以知道,caller可以提供给我们this。如果没有提供,则this为global object。问题又来了,caller是怎么提供this的? 
在11.2.3中,找到如下关于Function calls的描述:The production CallExpression : MemberExpression Arguments is evaluated as follows: 
1. Evaluate MemberExpression. 
2. Evaluate Arguments, producing an internal list of argument values (see 11.2.4). 
3. Call GetValue(Result(1)). 
4. If Type(Result(3)) is not Object, throw a TypeError exception. 
5. If Result(3) does not implement the internal [[Call]] method, throw a TypeError exception. 
6. If Type(Result(1)) is Reference, Result(6) is GetBase(Result(1)). Otherwise, Result(6) is null. 
7. If Result(6) is an activation object, Result(7) is null. Otherwise, Result(7) is the same as Result(6). 
8. Call the [[Call]] method on Result(3), providing Result(7) as the this value and providing the list Result(2) as the argument values. 
9. Return Result(8). 
从步骤6、7中可以看出来,如果MemberExpression的结果是一个Reference的话,提供的this应该是GetBase(Reference),否则是空。步骤7中还有描述了6的结果是活动对象的情况,我们这里忽略。 又有疑问了,Reference?Reference是什么,GetBase又是什么? 
我们在8.7中,找到了Reference的答案。这里的描述比较长,我只摘了可以满足我们需要的一段: A Reference is a reference to a property of an object. A Reference consists of two components, the base object and the property name. 
The following abstract operations are used in this specification to access the components of references: 
GetBase(V). Returns the base object component of the reference V. 
GetPropertyName(V). Returns the property name component of the reference V. 
已经很明显了,一个Reference必须引用一个对象的一个属性。所以我们通过obj.method()来调用的时候,obj.method这个表达式生成了一个中间态的Reference,这个Reference的base object就是obj,所以GetBase的结果就是obj,于是obj被caller提供作this 
我曾经看到很多文章,举了类似obj.method()这样的调用例子,认为obj就是caller,来解释这番话: 
The caller provides the this value. If the this value provided by the caller is not an object (note that null is not an object), then the this value is the global object. 
这其实是说不通的。 
caller绝不可能是obj,否则被attachEvent的函数或对象方法,他们运行时的this就解释不通了。 所以,通过我们自己代码调用的函数,caller由脚本引擎执行控制所决定;在浏览器宿主环境通过事件触发的,caller由浏览器控制的行为所决定。 
   关于原型链的补充——原型链会不会是圆形链 
这个问题是telei同学提出的。答案是:不会 
回头看看[[Construct]]的步骤,我们可以发现,创建一个对象obj时,obj.[[prototype]]成员被赋予其构造器的prototype成员。但是当构造器的prototype成员被指向为另外一个对象的引用时,obj.[[prototype]]依然是其构造器的前prototype对象。 
描述代码如下:(注释里是说明) 
function A(){ 
        this.testA = new Function(); 

function B(){ 
        this.testB = new Function(); 

 
var a = new A(); 
 
B.prototype = a; 
//a.[[prototype]] == {};(不是真的等,{}表示的是Function A初始的prototype object。下同) 
 
var b = new B(); 
//b.[[prototype]] == a; 
//b.[[prototype]].[[prototype]] == a.[[prototype]] == {}; 
 
A.prototype = b; 
 
var a2 = new A(); 
//a2.[[prototype]] == b; 
//a2.[[prototype]].[[prototype]] == b.[[prototype]] == a; 
//a2.[[prototype]].[[prototype]].[[prototype]] == b.[[prototype]].[[prototype]] == a.[[prototype]] == {}; 
 
//最后测试一下,很搞笑的 
alert(a instanceof A); 
最后特殊的解释:好吧,上面代码的最后出现了很搞笑的事情,合乎语言的实现,但不合乎正常以及不正常地球人的逻辑。 我们知道,a对象是被A构造器创建出来的,所以a是A的实例。 但是,上面类型判断那里有讲,instanceof是通过构造器prototype成员与对象原型链的比较来判断的。所以当对象a被创建后,如果创建它的构造器的prototype发生了变化,a就和他妈(构造器)没任何关系了。 看到这里,你确定你还想要在实例化对象后,修改构造器的prototype成另外一个对象吗? 

解决方案 »

  1.   

    看上去好乱!首先,在10.2.3中告诉我们: The caller provides the this value. If the this value provided by the caller is not an object (note that null is not an object), then the this value is the global object. 我们可以知道,caller可以提供给我们this。如果没有提供,则this为global object。问题又来了,caller是怎么提供this的? 
    11.2.3中,找到如下关于Function calls的描述:The production CallExpression : MemberExpression 而且这些红色部分也不知道在哪里?
      

  2.   

    baiduforum    baidu粉丝不过俺一般用GOOGLE