最近看了篇关于this和上下文环境的文章,觉得自己也有必要总结一下。
首先说明什么是上下文环境。JavaScript中的this关键字通常都会指向当前函数的拥有者,在JavaScript中通常把这个拥有者叫做执行上下文,函数的执行上下文由当前的执行环境来决定。另外就是要区别上下文环境和作用域:上下文环境是在执行时确定的,是可能改变的,而作用域是在定义时确定的,是不会变的。
再来说明的就是this在运行环境下到底是如何来确定上下文的:
在http://www.cnblogs.com/zhangle/archive/2010/07/01/1769435.html这篇bolg中,博主归纳了4点:
- 如果当前执行的是一个对象的方法,则执行上下文就是这个方法所属的对象
- 如果当前是一个创建一个对象的过程,则执行上下文就是这个正在被创建的对象
- 如果一个方法在执行时没有明确的指定附属的对像,则这个方法的上下文是全局对象
- 使用call和apply可以改变对象的执行上下文
与之相类似的我在《JavaScript语言精粹》4.3 调用 这一小节里也看到了作者Douglas Crockford关于this和上下文的更加专业的总结。书中Douglas说道:调用一个函数时,函数除了接收声明时定义的形式参数,还要接收两个附加的参数:this和arguments。而this的取值就取决于调用的模式。调用模式有四种,每种在初始化this关键字上是存在差异的。我惊奇的发现Douglas的这四种调用模式与上面提到的那位博主归纳的是极其相似的。这四种模式是:
- 方法调用模式:当一个对象的方法被调用时,this将会被绑定到这个对象上。
- 函数调用模式:当一个函数并非是一个对象的属性,那么它被调用时将会是这种调用模式。这时this被绑定到全局变量上。
- 构造器调用模式:当一个函数前面加上new来调用时,将创建一个新的对象,这个对象将会被添加到函数prototype的后面,并且函数里的this会被绑定到新的对象实例上。
- apply调用模式:它能改变this关键字的上下文。
接着我们通过代码来了解这四种调用模式:
代码一:
//this.value就是对象myObject的属性value的值
var myObject = {
value:0,
increment:function(inc){
this.value+=typeof inc==="number"?inc:1;
}
};
myObject.increment(1);
alert(myObject.value); //1
myObject.increment(2);
alert(myObject.value); //3
代码二:
var Quo = function(string){
this.status = string;
};
Quo.prototype.get_status=function{
return this.status;
};
//由于this是在运行时确定的,所以在此句this才初始化,由于函数前面有new,所以this被绑定到了myQuo对象上
var myQuo = new Quo("confused");
alert(myQuo.get_status()); //confused
代码三:
var Quo = function(string){
this.status = string;
};
Quo("hi")
alert(Quo.status); //undefined
alert(status); //hi
引用Douglas的说法,这是“语言设计上的一个错误”。这个错误导致对象方法的内部函数无法访问此对象的属性与方法,这很不好。但是有解决的办法。
代码四:
//解决办法:用that传递
myObject.double = function () {
var that=this; // 这是关键
var helper = function () {
that.value = add (that.value,that.value);
};
help();
};
myObject.double();
alert(myObkect.getValue()); // 6
关于apply和call就无需多言了……