X the Unknown Becoming an “X'' in an equation.

27三/083

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吧。

或许你还会对这些文章感兴趣:

喜欢这个文章吗?

考虑订阅我们的RSS Feed吧!

评论 (3) 引用 (0)
  1. 看不懂。。。

  2. 真有空,看来高程一点问题都没有~

  3. What you can do at home is different with what you can do in office…

    我实在不敢在办公室拿着一本本厚厚的书招摇过市……


Leave a comment

还没有引用.