介绍
在这篇文章里,我们将讨论跟执行上下文直接相关的更多细节。讨论的主题就是this关键字。实践证明,这个主题很难,在不同执行上下文中this的确定经常会发生问题。
许多程序员习惯的认为,在程序语言中,this关键字与面向对象程序开发紧密相关,其完全指向由构造器新创建的对象。在ECMAScript规范中也是这样实现的,但正如我们将看到那样,在ECMAScript中,this并不限于只用来指向新创建的对象。
英文翻译: Dmitry A. Soshnikov在Stoyan Stefanov的帮助下
发布: 2010-03-07
俄文原文: Dmitry A. Soshnikov
修正: Zeroglif
发布: 2009-06-28;
更新:2010-03-07
本文绝大部分内容参考了:?p=900
部分句子参考了:justin的中文翻译
复制代码
让我们更详细的了解一下,在ECMAScript中this到底是什么?
定义
this是执行上下文中的一个属性:
复制代码 代码如下:
activeExecutionContext = {
VO: {...},
this: thisValue
};
这里VO是我们前一章讨论的变量对象。
this与上下文中可执行代码的类型有直接关系,this值在进入上下文时确定,并且在上下文运行期间永久不变。
下面让我们更详细研究这些案例:
全局代码中的this
在这里一切都简单。在全局代码中,this始终是全局对象本身,这样就有可能间接的引用到它了。
复制代码 代码如下:
// 显示定义全局对象的属性
this.a = 10; // global.a = 10
alert(a); // 10
// 通过赋值给一个无标示符隐式
b = 20;
alert(this.b); // 20
// 也是通过变量声明隐式声明的
// 因为全局上下文的变量对象是全局对象自身
var c = 30;
alert(this.c); // 30
函数代码中的this
在函数代码中使用this时很有趣,这种情况很难且会导致很多问题。
这种类型的代码中,this值的首要特点(或许是最主要的)是它不是静态的绑定到一个函数。
正如我们上面曾提到的那样,this是进入上下文时确定,在一个函数代码中,这个值在每一次完全不同。
不管怎样,在代码运行时的this值是不变的,也就是说,因为它不是一个变量,就不可能为其分配一个新值(相反,在Python编程语言中,它明确的定义为对象本身,在运行期间可以不断改变)。
复制代码 代码如下:
var foo = {x: 10};
var bar = {
x: 20,
test: function () {
alert(this === bar); // true
alert(this.x); // 20
this = foo; // 错误,任何时候不能改变this的值
alert(this.x); // 如果不出错的话,应该是10,而不是20
}
};
// 在进入上下文的时候
// this被当成bar对象
// determined as "bar" object; why so - will
// be discussed below in detail
bar.test(); // true, 20
foo.test = bar.test;
// 不过,这里this依然不会是foo
// 尽管调用的是相同的function
foo.test(); // false, 10
那么,影响了函数代码中this值的变化有几个因素:
首先,在通常的函数调用中,this是由激活上下文代码的调用者来提供的,即调用函数的父上下文(parent context )。this取决于调用函数的方式。
为了在任何情况下准确无误的确定this值,有必要理解和记住这重要的一点。正是调用函数的方式影响了调用的上下文中的this值,没有别的什么(我们可以在一些文章,甚至是在关于javascript的书籍中看到,它们声称:“this值取决于函数如何定义,如果它是全局函数,this设置为全局对象,如果函数是一个对象的方法,this将总是指向这个对象。–这绝对不正确”)。继续我们的话题,可以看到,即使是正常的全局函数也会被调用方式的不同形式激活,这些不同的调用方式导致了不同的this值。
复制代码 代码如下:
function foo() {
alert(this);
}
foo(); // global
alert(foo === foo.prototype.constructor); // true
// 但是同一个function的不同的调用表达式,this是不同的
foo.prototype.constructor(); // foo.prototype
有可能作为一些对象定义的方法来调用函数,但是this将不会设置为这个对象。
复制代码 代码如下:
var foo = {
bar: function () {
alert(this);
alert(this === foo);
}
};
foo.bar(); // foo, true
var exampleFunc = foo.bar;
alert(exampleFunc === foo.bar); // true
// 再一次,同一个function的不同的调用表达式,this是不同的
exampleFunc(); // global, false
那么,调用函数的方式如何影响this值?为了充分理解this值的确定,需要详细分析其内部类型之一——引用类型(Reference type)。
引用类型(Reference type)
使用伪代码我们可以将引用类型的值可以表示为拥有两个属性的对象——base(即拥有属性的那个对象),和base中的propertyName 。
复制代码 代码如下: