[百度分享]javascript中函数调用过程中的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由浏览器控制的行为所决定。
------------------- 切割线:坚持坚持,好不容易想写点正经东西,快了 ----------------------
9. 关于原型链的补充——原型链会不会是圆形链
这个问题是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就和他妈(构造器)没任何关系了。 看到这里,你确定你还想要在实例化对象后,修改构造器的pr