this
是Javascript
语言的一个关键字。
它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。比如,
function test() {
this.x = 1;
}
随着函数使用场合的不同,this
的值会发生变化。但是有一个总的原则,那就是this
指的是,调用函数的那个对象。
下面分四种情况,详细讨论this
的用法。
情况一:纯粹的函数调用
这是函数的最通常用法,属于全局性调用,因此this
就代表全局对象Global
。
function test(){
this.x = 1;
alert(this.x);
}
test(); // 1
为了证明this
就是全局对象,我对代码做一些改变:
var x = 1;
function test(){
alert(this.x);
}
test(); // 1
运行结果还是1
。再变一下:
var x = 1;
function test(){
this.x = 0;
}
test();
alert(x); //0
这时内部函数不能访问外部函数的this
和arguments
,解决方法是在外部函数定义一个变量并给它赋值为this
。
情况二:作为对象方法的调用
函数还可以作为某个对象的方法调用,这时this
就指这个上级对象。
function test(){
alert(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m(); // 1
情况三 作为构造函数调用
所谓构造函数,就是通过这个函数生成一个新对象(object)。这时,this
就指这个新对象。
function test(){
this.x = 1;
}
var o = new test();
alert(o.x); // 1
运行结果为1。为了表明这时this
不是全局对象,我对代码做一些改变:
var x = 2;
function test(){
this.x = 1;
}
var o = new test();
alert(x); // 2
运行结果为2
,表明全局变量x
的值根本没变。
情况四 apply调用
apply()
是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此this
指的就是这第一个参数。
var x = 0;
function test() {
alert(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m.apply(); // 0
apply()
的参数为空时,默认调用全局对象。因此,这时的运行结果为0
,证明this
指的是全局对象。
如果把最后一行代码修改为
o.m.apply(o); // 1
运行结果就变成了1
,证明了这时this
代表的是对象o
。
情况五:箭头函数
函数体内的this
对象,绑定定义时所在的对象,而不是使用时所在的对象。第一段代码中如果没有用self
代替this
,那么setTimeout
函数内function
中的this
将指向全局对象。
function foo() {
var self = this;
setTimeout(function() {
console.log("id:", self.id);
}, 100);
}
foo.call( { id: 42 } );
// id: 42
function foo() {
setTimeout( () => {
console.log("id:", this.id);
}, 100);
}
foo.call( { id: 42 } );
// id: 42
箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的this
是词法作用域,由上下文确定(词法作用域,即变量的作用域在函数定义的时候就决定了)。
由于 JavaScript 函数对this
绑定的错误处理,下面的例子无法得到预期结果:
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = function () {
return new Date().getFullYear() - this.birth; // this指向window或undefined
};
return fn();
}
};
现在,箭头函数完全修复了this
的指向,this
总是指向词法作用域,也就是外层调用者obj
:
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
return fn();
}
};
obj.getAge(); // 25
如果使用箭头函数,以前的那种 hack 写法:
var that = this;
就不再需要了。
由于this
在箭头函数中已经按照词法作用域绑定了,所以,用call()
或者apply()
调用箭头函数时,无法对this
进行绑定,即传入的第一个参数被忽略:
var obj = {
birth: 1990,
getAge: function (year) {
var b = this.birth; // 1990
var fn = (y) => y - this.birth; // this.birth仍是1990
return fn.call({birth:2000}, year);
}
};
obj.getAge(2015); // 25
对this
指向的详细说明请看:从 ECMAScript 规范解读 this