javascript里面的this
昨天的思考中遇到了this的问题。ECMA-262中,this是在执行上下文(Execution Contexts)中被首次提及的,所以或许我应该先了解一下什么是执行上下文。
Excution Contexts(我简称EC吧),当程序的控制流进入一段ECMAScript可执行代码时,就会进入一个EC。活动EC在逻辑上会形成一个栈,栈顶EC就是当前运行的EC。
ECMAScript可执行代码?
ECMA-262中定义了三类这样的可执行代码:
- 全局代码:用C的概念类比的话,就是main函数里面的所有语句。javascript程序的源代码,除了函数体以外,都可以看成是全局代码。
我好像一直在混淆javascript和ECMAScript的概念了?其实ECMAScript可以认为是javascript和jscript的泛化,是一个标准化的规范。虽然我不知道javascript是不是完全兼容于ECMA的规范,不过ECMA中定义的大部分概念,对javascript应该都是适用的。
- eval代码:eval,但凡解释性语言基本都有的东西。当然apply也是。eval/apply可谓亲密无间的伙伴,不过在这里它不是主角。eval代码就是应用到内建的eval函数的那些字符串。性质类似于全局代码。
- 函数代码:全局代码里面并没有包括函数体的源代码,函数体的源代码属于函数代码的一部分。当然,函数体的源代码中如果包含嵌套的函数体,嵌套的那部分则是属于另一段的函数代码的。上一篇文章里面说过函数也可以通过new Function来创建,所以new Function的最后一个参数,也就是函数体,也会被看作是一段函数代码。
我猜想EC的并非以一种对象实现的。ECMA-262里面从头到尾都没有提起过所谓execution context object,甚至连context object的概念也没有看见。会有不少对象和引用附加在一个EC上,大概可以把EC理解成一个小型集合吧。
那么this到底和EC有什么关系?
this是EC上附加的一个……引用。this的值与调用者(主要对函数而言)和被执行的代码有关,并且值在进入EC的时候就已经定下来了。this是不可修改的。
this是跟可执行代码类型有关的:
- 对于全局代码,this引用到全局对象。
什么是全局对象?
全局对象是javascript的一类原生对象Global Object,在进入EC之前就已经被创建。它有几个特点:
- 不能通过new constructor构造。
- 不能用作函数调用。
- Global Object的类型以及prototype是跟具体的javascript的实现细节相关的。
javascript提供的内建值(NaN,Infinity和undefined)、内建函数(eval,parseInt等)以及其他原生对象的构造函数(Object,Function等)都是Global Object的提供的属性。可以向Global Object中添加各种其他的属性。
Global Object处于作用域链的最高层。至于什么是作用域链……再说吧,现在已经离题很远了。
根据这些,现在可以猜想到的一点就是,在javascript里面这样的调用:
eval("alert('hello');");
其实是等价于下面这样的伪代码:
global_object.eval("alert('hello');");
回到this。因为对于全局代码,this引用到了全局对象,所以在全局代码里面使用this,跟没使用效果是一样的orz:
this.a="hello";
this.alert(a===this.a); - eval代码
当进入的是eval代码的EC时,上一个EC,被称作调用上下文。eval代码里面的this,与调用上下文里面的this是一样的。如果没有调用上下文,那么eval代码会做与全局代码一样的处理。
function test() {
eval("this.a = 'hello';");
alert(a);
}
test();不过实际上,这里的this用了跟没有用也是一样的。
- 函数代码
this的值由调用者提供。当调用者不是对象时,this将引用到全局对象上。
看到这里,虽然还没弄清楚到底调用者怎样提供this的值,不过可以猜想,如果调用者是对象的话,那么this将会引用到调用者自身。这就可以解释为什么new constructor的时候,this是指向实例化的对象了。如果作为普通函数调用,按照之前对全局代码得出的结论,应该跟global_object.a_function的调用是一样的。这样想的话,调用者就是全局对象了。
那样的话,总算明白了普通函数里面的this有什么意思了……
function test_class () {
this.v1 = '1';
}/* new constructor方式调用,function code的
调用者为新实例化的对象,this引用到这个对象上。*/
alert((new test_class()).v1);alert(v1); // 调用失败,v1未定义
test_class(); // 普通函数调用,this为全局对象
alert(v1); // 经过上面的调用,全局对象里面有了v1的定义
不过让ECMA-262里面提到的一点我不太明白:eval代码可能会没有调用上下文。那会是什么样的情况呢?是指eval语句是第一句的情况吗?还是说只有eval一句的情况呢?
说到这里仍然是有很多不明不白,唯一清晰了的就是this在普通函数里面有什么含意……嘛既然标题是关于this的,我也不去想先了。留给后一篇post吧。
2008年03月30日 19:38
看不懂。。。
2008年03月31日 23:12
真有空,看来高程一点问题都没有~
2008年04月01日 10:56
What you can do at home is different with what you can do in office…
我实在不敢在办公室拿着一本本厚厚的书招摇过市……